@ascendenceai/cortena-extensions-e2e-kit 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +7 -0
- package/README.md +126 -0
- package/dist/config.d.ts +59 -0
- package/dist/config.js +63 -0
- package/dist/config.js.map +1 -0
- package/dist/env.d.ts +43 -0
- package/dist/env.js +67 -0
- package/dist/env.js.map +1 -0
- package/dist/fixtures/consent.d.ts +107 -0
- package/dist/fixtures/consent.js +220 -0
- package/dist/fixtures/consent.js.map +1 -0
- package/dist/fixtures/embedded.d.ts +47 -0
- package/dist/fixtures/embedded.js +60 -0
- package/dist/fixtures/embedded.js.map +1 -0
- package/dist/fixtures/list-state.d.ts +43 -0
- package/dist/fixtures/list-state.js +58 -0
- package/dist/fixtures/list-state.js.map +1 -0
- package/dist/fixtures/popup.d.ts +44 -0
- package/dist/fixtures/popup.js +81 -0
- package/dist/fixtures/popup.js.map +1 -0
- package/dist/fixtures/shell.d.ts +63 -0
- package/dist/fixtures/shell.js +96 -0
- package/dist/fixtures/shell.js.map +1 -0
- package/dist/fixtures/sign-in.d.ts +59 -0
- package/dist/fixtures/sign-in.js +115 -0
- package/dist/fixtures/sign-in.js.map +1 -0
- package/dist/fixtures/theme.d.ts +42 -0
- package/dist/fixtures/theme.js +79 -0
- package/dist/fixtures/theme.js.map +1 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/dist/locators.d.ts +210 -0
- package/dist/locators.js +212 -0
- package/dist/locators.js.map +1 -0
- package/dist/protocol/suite.d.ts +31 -0
- package/dist/protocol/suite.js +196 -0
- package/dist/protocol/suite.js.map +1 -0
- package/dist/protocol/types.d.ts +61 -0
- package/dist/protocol/types.js +4 -0
- package/dist/protocol/types.js.map +1 -0
- package/package.json +58 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { expect } from '@playwright/test';
|
|
2
|
+
import { SHELL } from '../locators.js';
|
|
3
|
+
export function shell(page) {
|
|
4
|
+
const avatarTrigger = page.locator(SHELL.avatarTrigger);
|
|
5
|
+
const helpButton = page.locator(SHELL.helpButton);
|
|
6
|
+
const helpPanel = page.locator(SHELL.helpPanel);
|
|
7
|
+
const navDrawer = page.locator(SHELL.navDrawer);
|
|
8
|
+
const navToggle = page.locator(SHELL.navToggle);
|
|
9
|
+
return {
|
|
10
|
+
root: page.locator(SHELL.root),
|
|
11
|
+
header: page.locator(SHELL.header),
|
|
12
|
+
brand: page.locator(SHELL.brand),
|
|
13
|
+
brandName: page.locator(SHELL.brandName),
|
|
14
|
+
brandSquare: page.locator(SHELL.brandSquare),
|
|
15
|
+
brandMark: page.locator(SHELL.brandMark).first(),
|
|
16
|
+
nav: page.locator(SHELL.nav),
|
|
17
|
+
navItems: page.locator(SHELL.navItem),
|
|
18
|
+
navItem: (label) => page.locator(SHELL.navItem).filter({ hasText: label }),
|
|
19
|
+
navItemById: (id) => page.locator(SHELL.navItemById(id)),
|
|
20
|
+
currentNavItem: page.locator(SHELL.navItemCurrent),
|
|
21
|
+
navToggle,
|
|
22
|
+
navDrawer,
|
|
23
|
+
navDrawerClose: page.locator(SHELL.navDrawerClose),
|
|
24
|
+
main: page.locator(SHELL.main),
|
|
25
|
+
avatarTrigger,
|
|
26
|
+
menuItem: (id) => page.locator(SHELL.menuItem(id)),
|
|
27
|
+
cluster: page.locator(SHELL.cluster),
|
|
28
|
+
themeToggle: page.locator(SHELL.themeToggle),
|
|
29
|
+
helpButton,
|
|
30
|
+
helpPanel,
|
|
31
|
+
async openAvatarMenu() {
|
|
32
|
+
await avatarTrigger.click();
|
|
33
|
+
await expect(page.locator(SHELL.menuItem('logout'))).toBeVisible();
|
|
34
|
+
},
|
|
35
|
+
async openHelp() {
|
|
36
|
+
await helpButton.click();
|
|
37
|
+
await expect(helpPanel).toBeVisible();
|
|
38
|
+
},
|
|
39
|
+
/**
|
|
40
|
+
* The viewport stays narrow afterwards. Deliberate: the drawer is the last
|
|
41
|
+
* thing the shell spec asserts, and a helper that silently restored it
|
|
42
|
+
* would make a following assertion pass at a width the caller did not
|
|
43
|
+
* choose. Set it back explicitly if more follows.
|
|
44
|
+
*/
|
|
45
|
+
async openNavDrawer() {
|
|
46
|
+
/*
|
|
47
|
+
* The drawer is not a second navigation: it is the same nav, below the
|
|
48
|
+
* shell's breakpoint. The viewport is narrowed here rather than in a
|
|
49
|
+
* third Playwright project, because two projects are already both themes
|
|
50
|
+
* and a third dimension would be eight runs of the six specs for one
|
|
51
|
+
* assertion about a focus trap.
|
|
52
|
+
*/
|
|
53
|
+
await page.setViewportSize({ width: 480, height: 900 });
|
|
54
|
+
await expect(navToggle).toBeVisible();
|
|
55
|
+
await navToggle.click();
|
|
56
|
+
await expect(navDrawer).toBeVisible();
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* §10 / P-10, in one call: the mark and the name top-left, the destinations in
|
|
62
|
+
* the sidebar, **Settings under the avatar and nowhere else**, and the theme
|
|
63
|
+
* and help controls in the bottom-right cluster.
|
|
64
|
+
*
|
|
65
|
+
* The "and nowhere else" half is the one that decays. An extension adds a
|
|
66
|
+
* settings link to its sidebar because it is convenient, and the fleet stops
|
|
67
|
+
* having one place settings lives; asserting the absence is the only way that
|
|
68
|
+
* gets noticed.
|
|
69
|
+
*/
|
|
70
|
+
export async function expectShellChrome(page, expectations) {
|
|
71
|
+
const s = shell(page);
|
|
72
|
+
await expect(s.root).toBeVisible();
|
|
73
|
+
await expect(s.brand).toBeVisible();
|
|
74
|
+
await expect(s.brandName).toHaveText(expectations.brandName);
|
|
75
|
+
await expect(s.brandSquare).toBeVisible();
|
|
76
|
+
for (const label of expectations.navItems) {
|
|
77
|
+
await expect(s.navItem(label).first()).toBeVisible();
|
|
78
|
+
}
|
|
79
|
+
// Settings is not a destination in the sidebar (§10.2, mockup decision B1).
|
|
80
|
+
await expect(s.nav.getByText(/^settings$/i)).toHaveCount(0);
|
|
81
|
+
await s.openAvatarMenu();
|
|
82
|
+
await expect(s.menuItem('settings')).toBeVisible();
|
|
83
|
+
await expect(s.menuItem('logout')).toBeVisible();
|
|
84
|
+
for (const id of expectations.menuItems ?? []) {
|
|
85
|
+
await expect(s.menuItem(id)).toBeVisible();
|
|
86
|
+
}
|
|
87
|
+
await page.keyboard.press('Escape');
|
|
88
|
+
// Theme then help, in that order, in the one cluster (§9.3).
|
|
89
|
+
await expect(s.cluster).toBeVisible();
|
|
90
|
+
await expect(s.themeToggle).toBeVisible();
|
|
91
|
+
await expect(s.helpButton).toBeVisible();
|
|
92
|
+
await s.openHelp();
|
|
93
|
+
await expect(s.helpPanel).toBeVisible();
|
|
94
|
+
await page.keyboard.press('Escape');
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=shell.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shell.js","sourceRoot":"","sources":["../../src/fixtures/shell.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { type Locator, type Page } from '@playwright/test';
|
|
2
|
+
import { type E2ECredentials } from '../env.js';
|
|
3
|
+
export interface SignInOptions {
|
|
4
|
+
/** Where to land. Defaults to `/`. */
|
|
5
|
+
route?: string;
|
|
6
|
+
/** Overrides the environment, for a spec that needs a second identity. */
|
|
7
|
+
credentials?: E2ECredentials;
|
|
8
|
+
/**
|
|
9
|
+
* Ignore `CORTENA_E2E_TOKEN` and drive the form. The login-page spec needs
|
|
10
|
+
* this: a token handoff proves the store reads the URL, not that anybody can
|
|
11
|
+
* actually sign in.
|
|
12
|
+
*/
|
|
13
|
+
useForm?: boolean;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Signs in against the test cortena-auth and lands on `route`.
|
|
17
|
+
*
|
|
18
|
+
* Two ways in, and the difference matters:
|
|
19
|
+
*
|
|
20
|
+
* - **A token.** `CORTENA_E2E_TOKEN` goes on the URL, which is what the app's
|
|
21
|
+
* auth store reads synchronously in `getInitialState()` before the first
|
|
22
|
+
* render (§7.3). It is the fast path and the one every spec that is not
|
|
23
|
+
* about signing in should take — a suite that types a password six times
|
|
24
|
+
* is a suite that fails six times when the password rotates.
|
|
25
|
+
* - **The form.** The organisation step, then the credentials step. This is
|
|
26
|
+
* the only thing that proves a person can get in, so the login spec uses
|
|
27
|
+
* it even when a token is available.
|
|
28
|
+
*
|
|
29
|
+
* The form is found through `SIGN_IN` and nowhere else: DESIGN-119 changes how
|
|
30
|
+
* the sign-in screen is mounted, and when it does this function keeps working
|
|
31
|
+
* because the shared `data-slot`s are tried first.
|
|
32
|
+
*/
|
|
33
|
+
export declare function signIn(page: Page, options?: SignInOptions): Promise<void>;
|
|
34
|
+
/**
|
|
35
|
+
* The organisation step, which decides *how* the org signs in before it asks
|
|
36
|
+
* for anything: password, Google, or both (P-08). An extension that has no
|
|
37
|
+
* such step — one org, or the org on the URL — simply has no such field, and
|
|
38
|
+
* this returns without failing.
|
|
39
|
+
*/
|
|
40
|
+
export declare function organisationStep(page: Page, orgId: string): Promise<boolean>;
|
|
41
|
+
/**
|
|
42
|
+
* Anything that means "this is the sign-in screen": the shared composition, or
|
|
43
|
+
* the extension's own organisation or password field.
|
|
44
|
+
*/
|
|
45
|
+
export declare function signInScreen(page: Page): Locator;
|
|
46
|
+
/**
|
|
47
|
+
* Whether a sign-in screen is on the page.
|
|
48
|
+
*
|
|
49
|
+
* It **waits**, and the timeout is the whole design. Asked right after a
|
|
50
|
+
* navigation the answer is always "no": a single-page app answers `goto` with
|
|
51
|
+
* an empty document and renders afterwards. So the positive case gets a real
|
|
52
|
+
* timeout, and a caller asserting the *absence* of a sign-in screen passes a
|
|
53
|
+
* short one — having already waited for something that proves the app rendered.
|
|
54
|
+
*/
|
|
55
|
+
export declare function isSignInScreen(page: Page, options?: {
|
|
56
|
+
timeout?: number;
|
|
57
|
+
}): Promise<boolean>;
|
|
58
|
+
/** `?token=…`, preserving whatever query the route already carried. */
|
|
59
|
+
export declare function withToken(route: string, token: string, refreshToken?: string): string;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { expect } from '@playwright/test';
|
|
2
|
+
import { SIGN_IN } from '../locators.js';
|
|
3
|
+
import { readCredentials } from '../env.js';
|
|
4
|
+
/**
|
|
5
|
+
* Signs in against the test cortena-auth and lands on `route`.
|
|
6
|
+
*
|
|
7
|
+
* Two ways in, and the difference matters:
|
|
8
|
+
*
|
|
9
|
+
* - **A token.** `CORTENA_E2E_TOKEN` goes on the URL, which is what the app's
|
|
10
|
+
* auth store reads synchronously in `getInitialState()` before the first
|
|
11
|
+
* render (§7.3). It is the fast path and the one every spec that is not
|
|
12
|
+
* about signing in should take — a suite that types a password six times
|
|
13
|
+
* is a suite that fails six times when the password rotates.
|
|
14
|
+
* - **The form.** The organisation step, then the credentials step. This is
|
|
15
|
+
* the only thing that proves a person can get in, so the login spec uses
|
|
16
|
+
* it even when a token is available.
|
|
17
|
+
*
|
|
18
|
+
* The form is found through `SIGN_IN` and nowhere else: DESIGN-119 changes how
|
|
19
|
+
* the sign-in screen is mounted, and when it does this function keeps working
|
|
20
|
+
* because the shared `data-slot`s are tried first.
|
|
21
|
+
*/
|
|
22
|
+
export async function signIn(page, options = {}) {
|
|
23
|
+
const credentials = options.credentials ?? readCredentials();
|
|
24
|
+
const route = options.route ?? '/';
|
|
25
|
+
if (credentials.token && !options.useForm) {
|
|
26
|
+
await page.goto(withToken(route, credentials.token));
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
if (!credentials.email || !credentials.password) {
|
|
30
|
+
throw new Error('signIn({ useForm: true }) needs CORTENA_E2E_USER_EMAIL and CORTENA_E2E_USER_PASSWORD. '
|
|
31
|
+
+ 'A token proves the URL handoff works; only the form proves a person can sign in.');
|
|
32
|
+
}
|
|
33
|
+
await page.goto(route);
|
|
34
|
+
await organisationStep(page, credentials.orgId);
|
|
35
|
+
await credentialsStep(page, credentials.email, credentials.password);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The organisation step, which decides *how* the org signs in before it asks
|
|
39
|
+
* for anything: password, Google, or both (P-08). An extension that has no
|
|
40
|
+
* such step — one org, or the org on the URL — simply has no such field, and
|
|
41
|
+
* this returns without failing.
|
|
42
|
+
*/
|
|
43
|
+
export async function organisationStep(page, orgId) {
|
|
44
|
+
/*
|
|
45
|
+
* Waited for, not counted. A single-page app answers `goto` with an empty
|
|
46
|
+
* document and renders afterwards, so a `count()` taken the instant the
|
|
47
|
+
* navigation resolves is a count of nothing — which reads as "this extension
|
|
48
|
+
* has no organisation step" and then fails four assertions later on
|
|
49
|
+
* something unrelated.
|
|
50
|
+
*/
|
|
51
|
+
if (!(await isSignInScreen(page))) {
|
|
52
|
+
throw new Error('organisationStep: no sign-in screen appeared. Either the app did not render, or it is '
|
|
53
|
+
+ 'already signed in.');
|
|
54
|
+
}
|
|
55
|
+
const field = page.getByLabel(SIGN_IN.own.orgLabel);
|
|
56
|
+
if (!(await field.isVisible().catch(() => false)))
|
|
57
|
+
return false;
|
|
58
|
+
await field.fill(orgId);
|
|
59
|
+
await page.getByRole('button', { name: SIGN_IN.own.orgSubmit }).click();
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
async function credentialsStep(page, email, password) {
|
|
63
|
+
const emailField = page.getByLabel(SIGN_IN.own.emailLabel);
|
|
64
|
+
await emailField.waitFor({ state: 'visible' });
|
|
65
|
+
await emailField.fill(email);
|
|
66
|
+
await page.getByLabel(SIGN_IN.own.passwordLabel).fill(password);
|
|
67
|
+
const shared = page.locator(SIGN_IN.shared.submit);
|
|
68
|
+
if (await shared.isVisible().catch(() => false))
|
|
69
|
+
await shared.click();
|
|
70
|
+
else
|
|
71
|
+
await page.getByRole('button', { name: SIGN_IN.own.submit }).click();
|
|
72
|
+
/*
|
|
73
|
+
* Signed in is the *absence* of the form, not the presence of a heading: the
|
|
74
|
+
* heading is the extension's and the kit does not know it. An error banner is
|
|
75
|
+
* asserted against separately so a wrong password reports itself rather than
|
|
76
|
+
* timing out on something invisible.
|
|
77
|
+
*/
|
|
78
|
+
await expect(page.locator(SIGN_IN.shared.error)).toHaveCount(0);
|
|
79
|
+
await expect(page.getByLabel(SIGN_IN.own.passwordLabel)).toHaveCount(0, { timeout: 30_000 });
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Anything that means "this is the sign-in screen": the shared composition, or
|
|
83
|
+
* the extension's own organisation or password field.
|
|
84
|
+
*/
|
|
85
|
+
export function signInScreen(page) {
|
|
86
|
+
return page
|
|
87
|
+
.locator(SIGN_IN.shared.screen)
|
|
88
|
+
.or(page.locator(SIGN_IN.shared.form))
|
|
89
|
+
.or(page.getByLabel(SIGN_IN.own.orgLabel))
|
|
90
|
+
.or(page.getByLabel(SIGN_IN.own.passwordLabel));
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Whether a sign-in screen is on the page.
|
|
94
|
+
*
|
|
95
|
+
* It **waits**, and the timeout is the whole design. Asked right after a
|
|
96
|
+
* navigation the answer is always "no": a single-page app answers `goto` with
|
|
97
|
+
* an empty document and renders afterwards. So the positive case gets a real
|
|
98
|
+
* timeout, and a caller asserting the *absence* of a sign-in screen passes a
|
|
99
|
+
* short one — having already waited for something that proves the app rendered.
|
|
100
|
+
*/
|
|
101
|
+
export async function isSignInScreen(page, options = {}) {
|
|
102
|
+
try {
|
|
103
|
+
await signInScreen(page).first().waitFor({ state: 'visible', timeout: options.timeout ?? 15_000 });
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/** `?token=…`, preserving whatever query the route already carried. */
|
|
111
|
+
export function withToken(route, token, refreshToken = 'none') {
|
|
112
|
+
const separator = route.includes('?') ? '&' : '?';
|
|
113
|
+
return `${route}${separator}token=${encodeURIComponent(token)}&refreshToken=${encodeURIComponent(refreshToken)}`;
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=sign-in.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sign-in.js","sourceRoot":"","sources":["../../src/fixtures/sign-in.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { type Page } from '@playwright/test';
|
|
2
|
+
export type ThemeMode = 'light' | 'dark';
|
|
3
|
+
/**
|
|
4
|
+
* `attribute` — the user chose, so `data-theme` is written on `<html>`.
|
|
5
|
+
* `os` — the user did not, so there is no attribute and the painted
|
|
6
|
+
* theme comes from `prefers-color-scheme`.
|
|
7
|
+
*/
|
|
8
|
+
export type ThemePath = 'attribute' | 'os';
|
|
9
|
+
/**
|
|
10
|
+
* Puts the page in a theme, and asserts it got there.
|
|
11
|
+
*
|
|
12
|
+
* cortena-design drives dark mode off `data-theme` on `<html>` and falls back
|
|
13
|
+
* to `prefers-color-scheme` when the attribute is **absent**. Those are two
|
|
14
|
+
* different code paths and a suite that only ever writes the attribute never
|
|
15
|
+
* exercises the one a first-time visitor takes — which is how a fleet ends up
|
|
16
|
+
* with an extension that is correct in dark mode only for people who once
|
|
17
|
+
* pressed the toggle.
|
|
18
|
+
*
|
|
19
|
+
* So `theme()` does both:
|
|
20
|
+
*
|
|
21
|
+
* theme(page, 'dark') // the stored choice, data-theme="dark"
|
|
22
|
+
* theme(page, 'dark', { path: 'os' }) // nothing stored, OS says dark
|
|
23
|
+
*
|
|
24
|
+
* Call it **before** the first navigation: the choice is read out of
|
|
25
|
+
* localStorage before the first paint, so writing it afterwards proves only
|
|
26
|
+
* that a re-render happens.
|
|
27
|
+
*/
|
|
28
|
+
export declare function theme(page: Page, mode: ThemeMode, options?: {
|
|
29
|
+
path?: ThemePath;
|
|
30
|
+
storageKey?: string;
|
|
31
|
+
}): Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* What is actually painted, and how it got there.
|
|
34
|
+
*
|
|
35
|
+
* On the attribute path `<html data-theme>` says the mode outright. On the OS
|
|
36
|
+
* path the attribute is absent by design, so the evidence is the toggle's
|
|
37
|
+
* `data-theme-resolved` — which is `useCortenaTheme`'s own answer to "what am
|
|
38
|
+
* I painting", published for exactly this.
|
|
39
|
+
*/
|
|
40
|
+
export declare function expectTheme(page: Page, mode: ThemeMode, path?: ThemePath): Promise<void>;
|
|
41
|
+
/** The mode the project name encodes — `chromium-dark-1280` is the dark project. */
|
|
42
|
+
export declare function themeFromProjectName(name: string): ThemeMode;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { expect } from '@playwright/test';
|
|
2
|
+
import { SHELL, THEME } from '../locators.js';
|
|
3
|
+
/**
|
|
4
|
+
* Puts the page in a theme, and asserts it got there.
|
|
5
|
+
*
|
|
6
|
+
* cortena-design drives dark mode off `data-theme` on `<html>` and falls back
|
|
7
|
+
* to `prefers-color-scheme` when the attribute is **absent**. Those are two
|
|
8
|
+
* different code paths and a suite that only ever writes the attribute never
|
|
9
|
+
* exercises the one a first-time visitor takes — which is how a fleet ends up
|
|
10
|
+
* with an extension that is correct in dark mode only for people who once
|
|
11
|
+
* pressed the toggle.
|
|
12
|
+
*
|
|
13
|
+
* So `theme()` does both:
|
|
14
|
+
*
|
|
15
|
+
* theme(page, 'dark') // the stored choice, data-theme="dark"
|
|
16
|
+
* theme(page, 'dark', { path: 'os' }) // nothing stored, OS says dark
|
|
17
|
+
*
|
|
18
|
+
* Call it **before** the first navigation: the choice is read out of
|
|
19
|
+
* localStorage before the first paint, so writing it afterwards proves only
|
|
20
|
+
* that a re-render happens.
|
|
21
|
+
*/
|
|
22
|
+
export async function theme(page, mode, options = {}) {
|
|
23
|
+
const path = options.path ?? 'attribute';
|
|
24
|
+
const storageKey = options.storageKey ?? THEME.storageKey;
|
|
25
|
+
if (path === 'os') {
|
|
26
|
+
/*
|
|
27
|
+
* "The OS says dark" means the *stored choice is absent*. Leaving a
|
|
28
|
+
* previous spec's choice behind would make this assert the attribute path
|
|
29
|
+
* a second time under a different name.
|
|
30
|
+
*/
|
|
31
|
+
await page.addInitScript((key) => {
|
|
32
|
+
try {
|
|
33
|
+
window.localStorage.removeItem(key);
|
|
34
|
+
}
|
|
35
|
+
catch { /* a blocked store is still no choice */ }
|
|
36
|
+
}, storageKey);
|
|
37
|
+
await page.emulateMedia({ colorScheme: mode });
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
await page.addInitScript(([key, value]) => {
|
|
41
|
+
try {
|
|
42
|
+
window.localStorage.setItem(key, value);
|
|
43
|
+
}
|
|
44
|
+
catch { /* see above */ }
|
|
45
|
+
}, [storageKey, mode]);
|
|
46
|
+
/*
|
|
47
|
+
* The OS is set to the *opposite*. If the attribute path silently stopped
|
|
48
|
+
* working, a page whose OS agreed with the stored choice would still look
|
|
49
|
+
* right and the assertion below would pass for the wrong reason.
|
|
50
|
+
*/
|
|
51
|
+
await page.emulateMedia({ colorScheme: mode === 'dark' ? 'light' : 'dark' });
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* What is actually painted, and how it got there.
|
|
55
|
+
*
|
|
56
|
+
* On the attribute path `<html data-theme>` says the mode outright. On the OS
|
|
57
|
+
* path the attribute is absent by design, so the evidence is the toggle's
|
|
58
|
+
* `data-theme-resolved` — which is `useCortenaTheme`'s own answer to "what am
|
|
59
|
+
* I painting", published for exactly this.
|
|
60
|
+
*/
|
|
61
|
+
export async function expectTheme(page, mode, path = 'attribute') {
|
|
62
|
+
const html = page.locator('html');
|
|
63
|
+
if (path === 'attribute') {
|
|
64
|
+
await expect(html).toHaveAttribute(THEME.attribute, mode);
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
await expect(html).not.toHaveAttribute(THEME.attribute, /light|dark/);
|
|
68
|
+
}
|
|
69
|
+
const toggle = page.locator(SHELL.themeToggle);
|
|
70
|
+
if ((await toggle.count()) > 0) {
|
|
71
|
+
await expect(toggle.first()).toHaveAttribute(THEME.resolvedAttribute, mode);
|
|
72
|
+
await expect(toggle.first()).toHaveAttribute(THEME.choiceAttribute, path === 'attribute' ? mode : 'system');
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/** The mode the project name encodes — `chromium-dark-1280` is the dark project. */
|
|
76
|
+
export function themeFromProjectName(name) {
|
|
77
|
+
return /dark/i.test(name) ? 'dark' : 'light';
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=theme.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"theme.js","sourceRoot":"","sources":["../../src/fixtures/theme.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAa,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAU9C;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,KAAK,UAAU,KAAK,CACzB,IAAU,EACV,IAAe,EACf,UAAqD,EAAE;IAEvD,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,WAAW,CAAC;IACzC,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,KAAK,CAAC,UAAU,CAAC;IAE1D,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QAClB;;;;WAIG;QACH,MAAM,IAAI,CAAC,aAAa,CAAC,CAAC,GAAG,EAAE,EAAE;YAC/B,IAAI,CAAC;gBAAC,MAAM,CAAC,YAAY,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,wCAAwC,CAAC,CAAC;QACjG,CAAC,EAAE,UAAU,CAAC,CAAC;QACf,MAAM,IAAI,CAAC,YAAY,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/C,OAAO;IACT,CAAC;IAED,MAAM,IAAI,CAAC,aAAa,CACtB,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE;QACf,IAAI,CAAC;YAAC,MAAM,CAAC,YAAY,CAAC,OAAO,CAAC,GAAa,EAAE,KAAe,CAAC,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC;IAChG,CAAC,EACD,CAAC,UAAU,EAAE,IAAI,CAAU,CAC5B,CAAC;IACF;;;;OAIG;IACH,MAAM,IAAI,CAAC,YAAY,CAAC,EAAE,WAAW,EAAE,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;AAC/E,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,IAAU,EAAE,IAAe,EAAE,OAAkB,WAAW;IAC1F,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAClC,IAAI,IAAI,KAAK,WAAW,EAAE,CAAC;QACzB,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC,eAAe,CAAC,KAAK,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IAC5D,CAAC;SAAM,CAAC;QACN,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,eAAe,CAAC,KAAK,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;IACxE,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IAC/C,IAAI,CAAC,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC;QAC/B,MAAM,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,eAAe,CAAC,KAAK,CAAC,iBAAiB,EAAE,IAAI,CAAC,CAAC;QAC5E,MAAM,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,eAAe,CAC1C,KAAK,CAAC,eAAe,EACrB,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CACvC,CAAC;IACJ,CAAC;AACH,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,oBAAoB,CAAC,IAAY;IAC/C,OAAO,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;AAC/C,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shared Playwright layer-3 kit — HOW-TO §23.4, audit rule P-36.
|
|
3
|
+
*
|
|
4
|
+
* Six extensions of seven had no end-to-end suite at all, because §23.4 names
|
|
5
|
+
* six flows that are the same for every one of them and each would have had to
|
|
6
|
+
* write all six from scratch: a sign-in helper, a theme switcher, a way to find
|
|
7
|
+
* the shell, a way to drive the agent pop-up. This is those six, written once.
|
|
8
|
+
* An extension supplies a config file and its own six values.
|
|
9
|
+
*
|
|
10
|
+
* Read `README.md` for the values. `locators.ts` is the file a cortena-ui
|
|
11
|
+
* change breaks, on purpose: once, here, rather than in six suites.
|
|
12
|
+
*/
|
|
13
|
+
export { readCredentials, hasCredentials, environmentName, MissingCredentialsError } from './env.js';
|
|
14
|
+
export type { E2ECredentials } from './env.js';
|
|
15
|
+
export { AGENT, CONSENT, CORTENA_UI, SESSION, SHELL, SIGN_IN, THEME } from './locators.js';
|
|
16
|
+
export { signIn, organisationStep, isSignInScreen, signInScreen, withToken } from './fixtures/sign-in.js';
|
|
17
|
+
export type { SignInOptions } from './fixtures/sign-in.js';
|
|
18
|
+
export { embedded, postHostTheme, expectTokenStrippedFromUrl } from './fixtures/embedded.js';
|
|
19
|
+
export type { EmbeddedOptions } from './fixtures/embedded.js';
|
|
20
|
+
export { theme, expectTheme, themeFromProjectName } from './fixtures/theme.js';
|
|
21
|
+
export type { ThemeMode, ThemePath } from './fixtures/theme.js';
|
|
22
|
+
export { shell, expectShellChrome } from './fixtures/shell.js';
|
|
23
|
+
export type { Shell, ShellExpectations } from './fixtures/shell.js';
|
|
24
|
+
export { popup, expectSessionResumes } from './fixtures/popup.js';
|
|
25
|
+
export type { Popup } from './fixtures/popup.js';
|
|
26
|
+
export { listState } from './fixtures/list-state.js';
|
|
27
|
+
export type { ListStateFilter, ListStateOptions } from './fixtures/list-state.js';
|
|
28
|
+
export { consent, authorizeUrl, deleteClient, discoverAuthorizationServer, discoverProtectedResource, registerClient, signInAtAuthorizationServer, PKCE_CHALLENGE, PKCE_VERIFIER, } from './fixtures/consent.js';
|
|
29
|
+
export type { AuthorizationServerMetadata, ClientRegistration, ConsentOptions, ProtectedResourceMetadata, } from './fixtures/consent.js';
|
|
30
|
+
export { defineProtocolSuite } from './protocol/suite.js';
|
|
31
|
+
export { PROTOCOL_CASE_KEYS } from './protocol/types.js';
|
|
32
|
+
export type { ProtocolCases, ProtocolExpectations, ProtocolSuiteOptions } from './protocol/types.js';
|
|
33
|
+
export { protocolConfig } from './config.js';
|
|
34
|
+
export type { ProtocolConfigOptions } from './config.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shared Playwright layer-3 kit — HOW-TO §23.4, audit rule P-36.
|
|
3
|
+
*
|
|
4
|
+
* Six extensions of seven had no end-to-end suite at all, because §23.4 names
|
|
5
|
+
* six flows that are the same for every one of them and each would have had to
|
|
6
|
+
* write all six from scratch: a sign-in helper, a theme switcher, a way to find
|
|
7
|
+
* the shell, a way to drive the agent pop-up. This is those six, written once.
|
|
8
|
+
* An extension supplies a config file and its own six values.
|
|
9
|
+
*
|
|
10
|
+
* Read `README.md` for the values. `locators.ts` is the file a cortena-ui
|
|
11
|
+
* change breaks, on purpose: once, here, rather than in six suites.
|
|
12
|
+
*/
|
|
13
|
+
export { readCredentials, hasCredentials, environmentName, MissingCredentialsError } from './env.js';
|
|
14
|
+
export { AGENT, CONSENT, CORTENA_UI, SESSION, SHELL, SIGN_IN, THEME } from './locators.js';
|
|
15
|
+
export { signIn, organisationStep, isSignInScreen, signInScreen, withToken } from './fixtures/sign-in.js';
|
|
16
|
+
export { embedded, postHostTheme, expectTokenStrippedFromUrl } from './fixtures/embedded.js';
|
|
17
|
+
export { theme, expectTheme, themeFromProjectName } from './fixtures/theme.js';
|
|
18
|
+
export { shell, expectShellChrome } from './fixtures/shell.js';
|
|
19
|
+
export { popup, expectSessionResumes } from './fixtures/popup.js';
|
|
20
|
+
export { listState } from './fixtures/list-state.js';
|
|
21
|
+
export { consent, authorizeUrl, deleteClient, discoverAuthorizationServer, discoverProtectedResource, registerClient, signInAtAuthorizationServer, PKCE_CHALLENGE, PKCE_VERIFIER, } from './fixtures/consent.js';
|
|
22
|
+
export { defineProtocolSuite } from './protocol/suite.js';
|
|
23
|
+
export { PROTOCOL_CASE_KEYS } from './protocol/types.js';
|
|
24
|
+
export { protocolConfig } from './config.js';
|
|
25
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,eAAe,EAAE,uBAAuB,EAAE,MAAM,UAAU,CAAC;AAGrG,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAE3F,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,cAAc,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAG1G,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,0BAA0B,EAAE,MAAM,wBAAwB,CAAC;AAG7F,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAG/E,OAAO,EAAE,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAG/D,OAAO,EAAE,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAGlE,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAGrD,OAAO,EACL,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,2BAA2B,EAAE,yBAAyB,EAC3F,cAAc,EAAE,2BAA2B,EAAE,cAAc,EAAE,aAAa,GAC3E,MAAM,uBAAuB,CAAC;AAK/B,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAGzD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC"}
|