Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions enterprise/e2e/html/hurl/schemas-as.all.hurl
Original file line number Diff line number Diff line change
Expand Up @@ -719,3 +719,73 @@ header "Vary" == "Accept, Accept-Encoding"
header "Date" matches /^(Mon|Tue|Wed|Thu|Fri|Sat|Sun), (0[1-9]|[12][0-9]|3[01]) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) [0-9]{4} ([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9] GMT$/
body contains "Draft3"
body contains "https://www.learnjsonschema.com/draft3"

# The web page offers every conversion the metadata advertises, oldest first
GET {{base}}/test/draft3/string
Accept: text/html
HTTP 200
Cache-Control: public, max-age=0, must-revalidate
Content-Type: text/html; charset=utf-8
Referrer-Policy: strict-origin-when-cross-origin
Content-Security-Policy: frame-ancestors 'none'
X-Frame-Options: DENY
[Asserts]
header "Vary" == "Accept, Accept-Encoding"
header "Date" matches /^(Mon|Tue|Wed|Thu|Fri|Sat|Sun), (0[1-9]|[12][0-9]|3[01]) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) [0-9]{4} ([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9] GMT$/
xpath "string(//button[@data-sourcemeta-ui-dropdown-toggle])" == "Convert"
xpath "string(//button[@data-sourcemeta-ui-dropdown-toggle]/@aria-expanded)" == "false"
xpath "count(//ul[@data-sourcemeta-ui-dropdown-menu]/li/a)" == 5
xpath "string(//ul[@data-sourcemeta-ui-dropdown-menu]/li[1]/a)" == "Draft4"
xpath "string(//ul[@data-sourcemeta-ui-dropdown-menu]/li[1]/a/@href)" == "/test/draft3/string.json?as=draft4"
xpath "string(//ul[@data-sourcemeta-ui-dropdown-menu]/li[2]/a)" == "Draft6"
xpath "string(//ul[@data-sourcemeta-ui-dropdown-menu]/li[2]/a/@href)" == "/test/draft3/string.json?as=draft6"
xpath "string(//ul[@data-sourcemeta-ui-dropdown-menu]/li[3]/a)" == "Draft7"
xpath "string(//ul[@data-sourcemeta-ui-dropdown-menu]/li[3]/a/@href)" == "/test/draft3/string.json?as=draft7"
xpath "string(//ul[@data-sourcemeta-ui-dropdown-menu]/li[4]/a)" == "2019-09"
xpath "string(//ul[@data-sourcemeta-ui-dropdown-menu]/li[4]/a/@href)" == "/test/draft3/string.json?as=2019-09"
xpath "string(//ul[@data-sourcemeta-ui-dropdown-menu]/li[5]/a)" == "2020-12"
xpath "string(//ul[@data-sourcemeta-ui-dropdown-menu]/li[5]/a/@href)" == "/test/draft3/string.json?as=2020-12"

# The dialect the schema already declares is left out, as a schema is
# already written in it
GET {{base}}/test/jsonld/draft7
Accept: text/html
HTTP 200
Cache-Control: public, max-age=0, must-revalidate
Content-Type: text/html; charset=utf-8
Referrer-Policy: strict-origin-when-cross-origin
Content-Security-Policy: frame-ancestors 'none'
X-Frame-Options: DENY
[Asserts]
header "Vary" == "Accept, Accept-Encoding"
header "Date" matches /^(Mon|Tue|Wed|Thu|Fri|Sat|Sun), (0[1-9]|[12][0-9]|3[01]) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) [0-9]{4} ([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9] GMT$/
xpath "string(//button[@data-sourcemeta-ui-dropdown-toggle])" == "Convert"
xpath "count(//ul[@data-sourcemeta-ui-dropdown-menu]/li/a)" == 2
xpath "string(//ul[@data-sourcemeta-ui-dropdown-menu]/li[1]/a)" == "2019-09"
xpath "string(//ul[@data-sourcemeta-ui-dropdown-menu]/li[1]/a/@href)" == "/test/jsonld/draft7.json?as=2019-09"
xpath "string(//ul[@data-sourcemeta-ui-dropdown-menu]/li[2]/a)" == "2020-12"
xpath "string(//ul[@data-sourcemeta-ui-dropdown-menu]/li[2]/a/@href)" == "/test/jsonld/draft7.json?as=2020-12"
xpath "count(//ul[@data-sourcemeta-ui-dropdown-menu]/li/a[text()='Draft7'])" == 0

# A schema on the newest dialect has nothing left to be offered, even though
# the API still serves it in that dialect
GET {{base}}/test/object
Accept: text/html
HTTP 200
Cache-Control: public, max-age=0, must-revalidate
Content-Type: text/html; charset=utf-8
Referrer-Policy: strict-origin-when-cross-origin
Content-Security-Policy: frame-ancestors 'none'
X-Frame-Options: DENY
[Asserts]
header "Vary" == "Accept, Accept-Encoding"
header "Date" matches /^(Mon|Tue|Wed|Thu|Fri|Sat|Sun), (0[1-9]|[12][0-9]|3[01]) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) [0-9]{4} ([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9] GMT$/
xpath "count(//button[@data-sourcemeta-ui-dropdown-toggle])" == 0
xpath "count(//ul[@data-sourcemeta-ui-dropdown-menu])" == 0
xpath "boolean(//a[@href='/test/object.json?bundle=1'])" == true

GET {{base}}/test/object.json?as=2020-12
HTTP 200
Content-Type: application/schema+json
[Asserts]
header "ETag" exists
200 changes: 200 additions & 0 deletions enterprise/e2e/html/playwright/convert.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
import { test, expect } from '@playwright/test';

test.describe('Schema Convert Dropdown', () => {
test('starts collapsed and lists every dialect the schema can be asked for',
async ({ page }) => {
await page.goto('/test/draft3/string');

const toggle = page.locator('[data-sourcemeta-ui-dropdown-toggle]');
const menu = page.locator('[data-sourcemeta-ui-dropdown-menu]');

// The menu is closed until asked for
await expect(toggle).toHaveAttribute('aria-expanded', 'false');
await expect(menu).not.toBeVisible();

await toggle.click();

await expect(toggle).toHaveAttribute('aria-expanded', 'true');
await expect(menu).toBeVisible();

// A Draft 3 schema can be asked for in every newer official dialect
const items = menu.locator('a');
await expect(items).toHaveText([
'Draft4', 'Draft6', 'Draft7', '2019-09', '2020-12'
]);
await expect(items.nth(0)).toHaveAttribute(
'href', '/test/draft3/string.json?as=draft4');
await expect(items.nth(1)).toHaveAttribute(
'href', '/test/draft3/string.json?as=draft6');
await expect(items.nth(2)).toHaveAttribute(
'href', '/test/draft3/string.json?as=draft7');
await expect(items.nth(3)).toHaveAttribute(
'href', '/test/draft3/string.json?as=2019-09');
await expect(items.nth(4)).toHaveAttribute(
'href', '/test/draft3/string.json?as=2020-12');
});

test('leaves out the dialect the schema already declares',
async ({ page }) => {
await page.goto('/test/jsonld/draft7');

const toggle = page.locator('[data-sourcemeta-ui-dropdown-toggle]');
const menu = page.locator('[data-sourcemeta-ui-dropdown-menu]');
await toggle.click();

const items = menu.locator('a');
await expect(items).toHaveText([ '2019-09', '2020-12' ]);
await expect(items.nth(0)).toHaveAttribute(
'href', '/test/jsonld/draft7.json?as=2019-09');
await expect(items.nth(1)).toHaveAttribute(
'href', '/test/jsonld/draft7.json?as=2020-12');
});

test('a schema on the newest dialect gets no control at all',
async ({ page }) => {
await page.goto('/test/object');

await expect(page.locator('[data-sourcemeta-ui-dropdown-toggle]'))
.toHaveCount(0);
await expect(page.locator('a[href="/test/object.json?bundle=1"]'))
.toBeVisible();
});

test('the menu sits below the button it belongs to', async ({ page }) => {
await page.goto('/test/draft3/string');

const toggle = page.locator('[data-sourcemeta-ui-dropdown-toggle]');
const menu = page.locator('[data-sourcemeta-ui-dropdown-menu]');
await toggle.click();

const toggleBox = await toggle.boundingBox();
const menuBox = await menu.boundingBox();
expect(menuBox.y).toBeGreaterThanOrEqual(toggleBox.y + toggleBox.height);
expect(menuBox.x).toBe(toggleBox.x);
});

test('clicking outside the dropdown closes it', async ({ page }) => {
await page.goto('/test/draft3/string');

const toggle = page.locator('[data-sourcemeta-ui-dropdown-toggle]');
const menu = page.locator('[data-sourcemeta-ui-dropdown-menu]');
await toggle.click();
await expect(menu).toBeVisible();

await page.locator('footer').click();

await expect(toggle).toHaveAttribute('aria-expanded', 'false');
await expect(menu).not.toBeVisible();
await expect(toggle).not.toBeFocused();
});

test('pressing escape from inside the menu hands focus back to the button',
async ({ page }) => {
await page.goto('/test/draft3/string');

const toggle = page.locator('[data-sourcemeta-ui-dropdown-toggle]');
const menu = page.locator('[data-sourcemeta-ui-dropdown-menu]');
await toggle.click();
await page.keyboard.press('Tab');
await expect(menu.locator('a').first()).toBeFocused();

await page.keyboard.press('Escape');

await expect(menu).not.toBeVisible();
await expect(toggle).toBeFocused();
});

test('tabbing past the last entry closes it and keeps going',
async ({ page }) => {
await page.goto('/test/draft3/string');

const toggle = page.locator('[data-sourcemeta-ui-dropdown-toggle]');
const menu = page.locator('[data-sourcemeta-ui-dropdown-menu]');
await toggle.click();

// Walk out of the menu through its five entries
for (let index = 0; index < 6; index++) {
await page.keyboard.press('Tab');
}

await expect(toggle).toHaveAttribute('aria-expanded', 'false');
await expect(menu).not.toBeVisible();

// Whatever follows the menu is where the walk was headed
await expect(page.locator('[data-sourcemeta-ui-tab-target="usage-cli"]'))
.toBeFocused();
await expect(toggle).not.toBeFocused();
});

test('moving focus out of an entry lands where it was aimed',
async ({ page }) => {
await page.goto('/test/draft3/string');

const toggle = page.locator('[data-sourcemeta-ui-dropdown-toggle]');
const menu = page.locator('[data-sourcemeta-ui-dropdown-menu]');
const bundle = page.locator('a[href="/test/draft3/string.json?bundle=1"]');
await toggle.click();
await page.keyboard.press('Tab');
await expect(menu.locator('a').first()).toBeFocused();

await bundle.focus();

await expect(menu).not.toBeVisible();
await expect(bundle).toBeFocused();
await expect(toggle).not.toBeFocused();
});

test('clicking the empty part of the menu leaves it open',
async ({ page }) => {
await page.goto('/test/draft3/string');

const toggle = page.locator('[data-sourcemeta-ui-dropdown-toggle]');
const menu = page.locator('[data-sourcemeta-ui-dropdown-menu]');
await toggle.click();

const box = await menu.boundingBox();
await page.mouse.click(box.x + box.width - 2, box.y + box.height - 2);

await expect(toggle).toHaveAttribute('aria-expanded', 'true');
await expect(menu).toBeVisible();
});

test('an entry navigates to the converted schema', async ({ page }) => {
await page.goto('/test/draft3/string');

const toggle = page.locator('[data-sourcemeta-ui-dropdown-toggle]');
const menu = page.locator('[data-sourcemeta-ui-dropdown-menu]');
await toggle.click();
await menu.locator('a', { hasText: '2020-12' }).click();

await expect(page).toHaveURL('/test/draft3/string.json?as=2020-12');
});

test('pressing escape closes it', async ({ page }) => {
await page.goto('/test/draft3/string');

const toggle = page.locator('[data-sourcemeta-ui-dropdown-toggle]');
const menu = page.locator('[data-sourcemeta-ui-dropdown-menu]');
await toggle.click();
await expect(menu).toBeVisible();

await page.keyboard.press('Escape');

await expect(toggle).toHaveAttribute('aria-expanded', 'false');
await expect(menu).not.toBeVisible();
});

test('clicking the button again closes it', async ({ page }) => {
await page.goto('/test/draft3/string');

const toggle = page.locator('[data-sourcemeta-ui-dropdown-toggle]');
const menu = page.locator('[data-sourcemeta-ui-dropdown-menu]');
await toggle.click();
await expect(menu).toBeVisible();

await toggle.click();

await expect(toggle).toHaveAttribute('aria-expanded', 'false');
await expect(menu).not.toBeVisible();
});
});
16 changes: 16 additions & 0 deletions enterprise/e2e/html/playwright/playwright.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { defineConfig } from '@playwright/test';

// See https://playwright.dev/docs/test-configuration
export default defineConfig({
testDir: '.',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: 'list',
outputDir: '../../../../build/test-results',
use: {
baseURL: process.env.PLAYWRIGHT_BASE_URL,
trace: 'on-first-retry'
}
});
1 change: 1 addition & 0 deletions src/web/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ sourcemeta_esbuild_bundle(
DEPENDS
"${CMAKE_CURRENT_SOURCE_DIR}/scripts/main.js"
"${CMAKE_CURRENT_SOURCE_DIR}/scripts/copy.js"
"${CMAKE_CURRENT_SOURCE_DIR}/scripts/dropdown.js"
"${CMAKE_CURRENT_SOURCE_DIR}/scripts/search.js"
"${CMAKE_CURRENT_SOURCE_DIR}/scripts/tabs.js"
"${CMAKE_CURRENT_SOURCE_DIR}/scripts/editor.js"
Expand Down
Loading
Loading