pomwright 1.3.0 → 1.5.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/AGENTS.md +37 -0
- package/CHANGELOG.md +193 -0
- package/README.md +316 -34
- package/dist/index.d.mts +1058 -132
- package/dist/index.d.ts +1058 -132
- package/dist/index.js +2309 -185
- package/dist/index.mjs +2304 -185
- package/docs/{get-locator-methods-explanation.md → v1/get-locator-methods-explanation.md} +0 -16
- package/docs/v1-to-v2-migration/bridge-migration-guide.md +159 -0
- package/docs/v1-to-v2-migration/direct-migration-guide.md +238 -0
- package/docs/v1-to-v2-migration/v1-to-v2-comparison.md +547 -0
- package/docs/v2/PageObject.md +293 -0
- package/docs/v2/composing-locator-modules.md +93 -0
- package/docs/v2/locator-registry.md +693 -0
- package/docs/v2/logging.md +168 -0
- package/docs/v2/overview.md +515 -0
- package/docs/v2/session-storage.md +160 -0
- package/index.ts +61 -9
- package/intTestV2/.env +0 -0
- package/intTestV2/fixtures/testApp.fixtures.ts +43 -0
- package/intTestV2/package.json +22 -0
- package/intTestV2/page-object-models/testApp/pages/iframe/iframe.locatorSchema.ts +24 -0
- package/intTestV2/page-object-models/testApp/pages/iframe/iframe.page.ts +17 -0
- package/intTestV2/page-object-models/testApp/pages/testPage.locatorSchema.ts +32 -0
- package/intTestV2/page-object-models/testApp/pages/testPage.page.ts +119 -0
- package/intTestV2/page-object-models/testApp/pages/testPath/[color]/color.locatorSchema.ts +29 -0
- package/intTestV2/page-object-models/testApp/pages/testPath/[color]/color.page.ts +48 -0
- package/intTestV2/page-object-models/testApp/pages/testPath/testPath.locatorSchema.ts +9 -0
- package/intTestV2/page-object-models/testApp/pages/testPath/testPath.page.ts +23 -0
- package/intTestV2/page-object-models/testApp/pages/testfilters/testfilters.locatorSchema.ts +114 -0
- package/intTestV2/page-object-models/testApp/pages/testfilters/testfilters.page.ts +23 -0
- package/intTestV2/page-object-models/testApp/testApp.base.ts +20 -0
- package/intTestV2/playwright.config.ts +54 -0
- package/intTestV2/server.js +216 -0
- package/intTestV2/test-data/staticPage/index.html +280 -0
- package/intTestV2/test-data/staticPage/w3images/avatar2.png +0 -0
- package/intTestV2/test-data/staticPage/w3images/avatar3.png +0 -0
- package/intTestV2/test-data/staticPage/w3images/avatar5.png +0 -0
- package/intTestV2/test-data/staticPage/w3images/avatar6.png +0 -0
- package/intTestV2/test-data/staticPage/w3images/forest.jpg +0 -0
- package/intTestV2/test-data/staticPage/w3images/lights.jpg +0 -0
- package/intTestV2/test-data/staticPage/w3images/mountains.jpg +0 -0
- package/intTestV2/test-data/staticPage/w3images/nature.jpg +0 -0
- package/intTestV2/test-data/staticPage/w3images/snow.jpg +0 -0
- package/intTestV2/tests/locatorRegistry/add/add.describe.spec.ts +54 -0
- package/intTestV2/tests/locatorRegistry/add/add.filter.spec.ts +143 -0
- package/intTestV2/tests/locatorRegistry/add/add.frameLocator.spec.ts +23 -0
- package/intTestV2/tests/locatorRegistry/add/add.getByAltText.spec.ts +23 -0
- package/intTestV2/tests/locatorRegistry/add/add.getById.spec.ts +45 -0
- package/intTestV2/tests/locatorRegistry/add/add.getByLabel.spec.ts +23 -0
- package/intTestV2/tests/locatorRegistry/add/add.getByPlaceholder.spec.ts +23 -0
- package/intTestV2/tests/locatorRegistry/add/add.getByRole.spec.ts +23 -0
- package/intTestV2/tests/locatorRegistry/add/add.getByTestId.spec.ts +23 -0
- package/intTestV2/tests/locatorRegistry/add/add.getByText.spec.ts +23 -0
- package/intTestV2/tests/locatorRegistry/add/add.getByTitle.spec.ts +23 -0
- package/intTestV2/tests/locatorRegistry/add/add.locator.spec.ts +23 -0
- package/intTestV2/tests/locatorRegistry/add/add.reuseExisting.spec.ts +66 -0
- package/intTestV2/tests/locatorRegistry/add/add.reuseReusable.spec.ts +311 -0
- package/intTestV2/tests/locatorRegistry/add/add.spec.ts +159 -0
- package/intTestV2/tests/locatorRegistry/filter.cycle.spec.ts +39 -0
- package/intTestV2/tests/locatorRegistry/getLocator/getLocator.spec.ts +253 -0
- package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.clearSteps.spec.ts +105 -0
- package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.describe.spec.ts +23 -0
- package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.filter.spec.ts +368 -0
- package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.getLocator.spec.ts +56 -0
- package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.getNestedLocator.spec.ts +175 -0
- package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.nth.spec.ts +60 -0
- package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.remove.spec.ts +32 -0
- package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.replace.spec.ts +24 -0
- package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.spec.ts +110 -0
- package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.update.spec.ts +322 -0
- package/intTestV2/tests/locatorRegistry/getNestedLocator/getNestedLocator.spec.ts +412 -0
- package/intTestV2/tests/locatorRegistry/registry/registry.binding.spec.ts +50 -0
- package/intTestV2/tests/locatorRegistry/validation/validation.locatorSchemaPath.spec.ts +115 -0
- package/intTestV2/tests/locatorRegistry/validation/validation.sub-path.spec.ts +45 -0
- package/intTestV2/tests/step/step.spec.ts +49 -0
- package/intTestV2/tests/testApp/color.spec.ts +15 -0
- package/intTestV2/tests/testApp/iframe.spec.ts +57 -0
- package/intTestV2/tests/testApp/testFilters.spec.ts +24 -0
- package/intTestV2/tests/testApp/testPage.spec.ts +161 -0
- package/intTestV2/tests/testApp/testPath.spec.ts +18 -0
- package/pack-build.sh +11 -0
- package/pack-test-v2.sh +36 -0
- package/package.json +10 -3
- package/playwright.base.ts +42 -0
- package/skills/README.md +56 -0
- package/skills/pomwright-v1-5-bridge-migration/SKILL.md +40 -0
- package/skills/pomwright-v1-5-bridge-migration/references/call-site-migration.md +178 -0
- package/skills/pomwright-v1-5-bridge-migration/references/schema-translation.md +183 -0
- package/skills/pomwright-v2-migration/SKILL.md +63 -0
- package/skills/pomwright-v2-migration/references/call-site-migration.md +265 -0
- package/skills/pomwright-v2-migration/references/class-migration.md +266 -0
- package/skills/pomwright-v2-migration/references/fixture-and-helpers.md +423 -0
- package/skills/pomwright-v2-migration/references/locator-registration.md +344 -0
- package/srcV2/fixture/base.fixtures.ts +23 -0
- package/srcV2/helpers/navigation.ts +153 -0
- package/srcV2/helpers/playwrightReportLogger.ts +196 -0
- package/srcV2/helpers/sessionStorage.ts +251 -0
- package/srcV2/helpers/stepDecorator.ts +106 -0
- package/srcV2/locators/index.ts +15 -0
- package/srcV2/locators/locatorQueryBuilder.ts +427 -0
- package/srcV2/locators/locatorRegistrationBuilder.ts +558 -0
- package/srcV2/locators/locatorRegistry.ts +541 -0
- package/srcV2/locators/locatorUpdateBuilder.ts +602 -0
- package/srcV2/locators/reusableLocatorBuilder.ts +200 -0
- package/srcV2/locators/types.ts +256 -0
- package/srcV2/locators/utils.ts +309 -0
- package/srcV2/locators/v1SchemaTranslator.ts +178 -0
- package/srcV2/pageObject.ts +105 -0
- /package/docs/{BaseApi-explanation.md → v1/BaseApi-explanation.md} +0 -0
- /package/docs/{BasePage-explanation.md → v1/BasePage-explanation.md} +0 -0
- /package/docs/{LocatorSchema-explanation.md → v1/LocatorSchema-explanation.md} +0 -0
- /package/docs/{LocatorSchemaPath-explanation.md → v1/LocatorSchemaPath-explanation.md} +0 -0
- /package/docs/{PlaywrightReportLogger-explanation.md → v1/PlaywrightReportLogger-explanation.md} +0 -0
- /package/docs/{intro-to-using-pomwright.md → v1/intro-to-using-pomwright.md} +0 -0
- /package/docs/{sessionStorage-methods-explanation.md → v1/sessionStorage-methods-explanation.md} +0 -0
- /package/docs/{tips-folder-structure.md → v1/tips-folder-structure.md} +0 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import type { LocatorRegistry } from "pomwright";
|
|
2
|
+
|
|
3
|
+
export type Paths =
|
|
4
|
+
| "one"
|
|
5
|
+
| "one.two"
|
|
6
|
+
| "body"
|
|
7
|
+
| "body.section"
|
|
8
|
+
| "body.section.heading"
|
|
9
|
+
| "body.section.button"
|
|
10
|
+
| "body.section@playground"
|
|
11
|
+
| "body.section@playground.heading"
|
|
12
|
+
| "body.section@playground.button"
|
|
13
|
+
| "body.section@playground.button@red"
|
|
14
|
+
| "body.section@playground.button@reset"
|
|
15
|
+
/**
|
|
16
|
+
* Fictional LocatorSchema do not exist in the DOM of the /testfilters page:
|
|
17
|
+
*/
|
|
18
|
+
| "fictional.filter@undefined"
|
|
19
|
+
| "fictional.filter@optionsUndefined"
|
|
20
|
+
| "fictional.locatorWithfilter@allOptions"
|
|
21
|
+
| "fictional.locatorAndOptionsWithfilter@allOptions"
|
|
22
|
+
| "fictional.filter@has"
|
|
23
|
+
| "fictional.filter@hasNot"
|
|
24
|
+
| "fictional.filter@hasText"
|
|
25
|
+
| "fictional.filter@hasNotText"
|
|
26
|
+
| "fictional.filter@hasNotText.filter@hasText"
|
|
27
|
+
| "fictional.filter@hasNotText.filter@hasText.filter@hasNotText"
|
|
28
|
+
| "fictional.filter@hasNotText.filter@hasText.filter@hasNotText.filter@hasText";
|
|
29
|
+
|
|
30
|
+
export function defineLocators(registry: LocatorRegistry<Paths>) {
|
|
31
|
+
registry.add("one").locator("div.one");
|
|
32
|
+
|
|
33
|
+
registry.add("one.two").locator("div.two").filter({ hasText: "two" }).nth(0);
|
|
34
|
+
|
|
35
|
+
registry.add("body").locator("body");
|
|
36
|
+
|
|
37
|
+
registry.add("body.section").locator("section");
|
|
38
|
+
|
|
39
|
+
registry.add("body.section.heading").getByRole("heading", { level: 2 });
|
|
40
|
+
|
|
41
|
+
registry.add("body.section.button").getByRole("button");
|
|
42
|
+
|
|
43
|
+
registry.add("body.section@playground").locator("section", { hasText: /Playground/i });
|
|
44
|
+
|
|
45
|
+
registry
|
|
46
|
+
.add("body.section@playground.heading")
|
|
47
|
+
.getByRole("heading", { name: "Primary Colors Playground", level: 2, exact: true });
|
|
48
|
+
|
|
49
|
+
registry.add("body.section@playground.button").getByRole("button");
|
|
50
|
+
|
|
51
|
+
registry.add("body.section@playground.button@red").getByRole("button", { name: "Red" });
|
|
52
|
+
|
|
53
|
+
registry.add("body.section@playground.button@reset").getByRole("button", { name: "Reset Color" });
|
|
54
|
+
|
|
55
|
+
/** --------------------------- Fictional LocatorSchema ---------------------------
|
|
56
|
+
* The following LocatorSchema DO NOT exist in the DOM of the /testfilters page
|
|
57
|
+
*
|
|
58
|
+
* They are used for testing that the getNestedLocator/getLocator methods correctly
|
|
59
|
+
* produces correct locator selector strings for LocatorSchema with/without filter.
|
|
60
|
+
*/
|
|
61
|
+
|
|
62
|
+
registry.add("fictional.filter@undefined").getByRole("button");
|
|
63
|
+
|
|
64
|
+
registry
|
|
65
|
+
.add("fictional.filter@optionsUndefined")
|
|
66
|
+
.getByRole("button")
|
|
67
|
+
.filter({ has: undefined, hasNot: undefined, hasText: undefined, hasNotText: undefined });
|
|
68
|
+
|
|
69
|
+
registry
|
|
70
|
+
.add("fictional.locatorWithfilter@allOptions")
|
|
71
|
+
.getByRole("button")
|
|
72
|
+
.filter({
|
|
73
|
+
has: "body.section",
|
|
74
|
+
hasNot: "body.section.button",
|
|
75
|
+
hasText: "hasText",
|
|
76
|
+
hasNotText: "hasNotText",
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
registry
|
|
80
|
+
.add("fictional.locatorAndOptionsWithfilter@allOptions")
|
|
81
|
+
.getByRole("button", { name: "roleOptions" })
|
|
82
|
+
.filter({
|
|
83
|
+
has: "body.section.heading",
|
|
84
|
+
hasNot: "body.section.button",
|
|
85
|
+
hasText: "hasText",
|
|
86
|
+
hasNotText: "hasNotText",
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
registry
|
|
90
|
+
.add("fictional.filter@has")
|
|
91
|
+
.getByRole("button")
|
|
92
|
+
.filter({ has: "body.section.heading" });
|
|
93
|
+
|
|
94
|
+
registry
|
|
95
|
+
.add("fictional.filter@hasNot")
|
|
96
|
+
.getByRole("button")
|
|
97
|
+
.filter({ hasNot: "body.section.button" });
|
|
98
|
+
|
|
99
|
+
registry.add("fictional.filter@hasText").getByRole("button").filter({ hasText: "hasText" });
|
|
100
|
+
|
|
101
|
+
registry.add("fictional.filter@hasNotText").getByRole("button").filter({ hasNotText: "hasNotText" });
|
|
102
|
+
|
|
103
|
+
registry.add("fictional.filter@hasNotText.filter@hasText").getByRole("button").filter({ hasText: "hasText" });
|
|
104
|
+
|
|
105
|
+
registry
|
|
106
|
+
.add("fictional.filter@hasNotText.filter@hasText.filter@hasNotText")
|
|
107
|
+
.getByRole("button")
|
|
108
|
+
.filter({ hasNotText: "hasNotText" });
|
|
109
|
+
|
|
110
|
+
registry
|
|
111
|
+
.add("fictional.filter@hasNotText.filter@hasText.filter@hasNotText.filter@hasText")
|
|
112
|
+
.getByRole("button")
|
|
113
|
+
.filter({ hasText: "hasText" });
|
|
114
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type Page, test } from "@playwright/test";
|
|
2
|
+
import TestApp from "../../testApp.base";
|
|
3
|
+
import { defineLocators, type Paths } from "./testfilters.locatorSchema";
|
|
4
|
+
|
|
5
|
+
export default class TestFiltersV2 extends TestApp<Paths> {
|
|
6
|
+
constructor(page: Page) {
|
|
7
|
+
super(page, "/testfilters");
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
protected defineLocators(): void {
|
|
11
|
+
defineLocators(this.locatorRegistry);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
protected pageActionsToPerformAfterNavigation(): (() => Promise<void>)[] | null {
|
|
15
|
+
return [];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async expectThisPage() {
|
|
19
|
+
await test.step(`Expect Page: ${this.urlPath}`, async () => {
|
|
20
|
+
await this.page.waitForURL(this.fullUrl);
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Page } from "@playwright/test";
|
|
2
|
+
import { type NavigationOptions, PageObject, type UrlPathTypeFromOptions, type UrlTypeOptions } from "pomwright";
|
|
3
|
+
|
|
4
|
+
type BaseOptions<Options extends UrlTypeOptions> = {
|
|
5
|
+
baseUrlType: string;
|
|
6
|
+
urlPathType: UrlPathTypeFromOptions<Options>;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export default abstract class TestApp<
|
|
10
|
+
Paths extends string,
|
|
11
|
+
Options extends UrlTypeOptions = { baseUrlType: string; urlPathType: string },
|
|
12
|
+
> extends PageObject<Paths, BaseOptions<Options>> {
|
|
13
|
+
protected constructor(
|
|
14
|
+
page: Page,
|
|
15
|
+
urlPath: UrlPathTypeFromOptions<BaseOptions<Options>>,
|
|
16
|
+
options?: { label?: string; navOptions?: NavigationOptions },
|
|
17
|
+
) {
|
|
18
|
+
super(page, "http://localhost:8080", urlPath, options);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { defineConfig, devices } from "@playwright/test";
|
|
2
|
+
import dotenv from "dotenv";
|
|
3
|
+
import { baseConfig } from "../playwright.base";
|
|
4
|
+
|
|
5
|
+
dotenv.config({ override: false, quiet: true });
|
|
6
|
+
|
|
7
|
+
export default defineConfig({
|
|
8
|
+
...baseConfig,
|
|
9
|
+
testDir: "./tests",
|
|
10
|
+
|
|
11
|
+
webServer: process.env.CI
|
|
12
|
+
? [
|
|
13
|
+
{
|
|
14
|
+
command: "pnpm start",
|
|
15
|
+
url: "http://localhost:8080/",
|
|
16
|
+
timeout: 5 * 60_000,
|
|
17
|
+
reuseExistingServer: false,
|
|
18
|
+
ignoreHTTPSErrors: false,
|
|
19
|
+
},
|
|
20
|
+
]
|
|
21
|
+
: [
|
|
22
|
+
{
|
|
23
|
+
command: "pnpm start",
|
|
24
|
+
url: "http://localhost:8080/",
|
|
25
|
+
timeout: 5 * 60_000,
|
|
26
|
+
reuseExistingServer: true,
|
|
27
|
+
ignoreHTTPSErrors: false,
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
|
|
31
|
+
projects: [
|
|
32
|
+
{
|
|
33
|
+
name: "chromium",
|
|
34
|
+
use: {
|
|
35
|
+
...(baseConfig.use ?? {}),
|
|
36
|
+
...devices["Desktop Chrome"],
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: "firefox",
|
|
41
|
+
use: {
|
|
42
|
+
...(baseConfig.use ?? {}),
|
|
43
|
+
...devices["Desktop Firefox"],
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
name: "webkit",
|
|
48
|
+
use: {
|
|
49
|
+
...(baseConfig.use ?? {}),
|
|
50
|
+
...devices["Desktop Safari"],
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
});
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
const express = require("express");
|
|
2
|
+
const path = require("node:path");
|
|
3
|
+
|
|
4
|
+
const app = express();
|
|
5
|
+
const port = 8080;
|
|
6
|
+
|
|
7
|
+
// Serve static files from the "test-data/staticPage" directory
|
|
8
|
+
app.use(express.static(path.join(__dirname, "test-data/staticPage")));
|
|
9
|
+
|
|
10
|
+
// Route to handle "/testpath/:color"
|
|
11
|
+
app.get("/testpath/:color", (req, res) => {
|
|
12
|
+
const color = req.params.color;
|
|
13
|
+
|
|
14
|
+
// Ensure the color is a valid 3 or 6-character hex code
|
|
15
|
+
if (!/^([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/.test(color)) {
|
|
16
|
+
res.status(400).send("Invalid color code.");
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Return a simple HTML page with the background set to the color
|
|
21
|
+
res.send(`
|
|
22
|
+
<html>
|
|
23
|
+
<body style="background-color: #${color}; height: 100vh; margin: 0; display: flex; justify-content: center; align-items: center; flex-direction: column;">
|
|
24
|
+
<h1 style="border: 3px solid black; padding: 10px; background-color: #E0E0E0; color: black;">Your Random Color is:</h1>
|
|
25
|
+
<table role="table" aria-label="Hex Code Information" style="text-align: center; font-size: 24px; background-color: #E0E0E0; color: black; border-collapse: collapse;">
|
|
26
|
+
<tr role="row">
|
|
27
|
+
<td role="rowheader" aria-label="Hex Code Header" style="border: 3px solid black; padding: 10px; background-color: silver;">Hex Code:</td>
|
|
28
|
+
</tr>
|
|
29
|
+
<tr role="row">
|
|
30
|
+
<td role="cell" aria-label="color code" style="border: 3px solid black; padding: 10px;">#${color}</td>
|
|
31
|
+
</tr>
|
|
32
|
+
</table>
|
|
33
|
+
</body>
|
|
34
|
+
</html>
|
|
35
|
+
`);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Route to handle "/testpath" with a link that generates a new random color on click
|
|
39
|
+
app.get("/testpath", (req, res) => {
|
|
40
|
+
res.send(`
|
|
41
|
+
<html>
|
|
42
|
+
<body style="display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0;">
|
|
43
|
+
<a id="randomColorLink" href="#" style="font-size: 24px;" aria-label="Random Color Link">Go to Random Color Page</a>
|
|
44
|
+
|
|
45
|
+
<script>
|
|
46
|
+
// Function to generate a random hex color of either 3 or 6 digits
|
|
47
|
+
function generateRandomColor() {
|
|
48
|
+
// Randomly choose between generating a 3-character or 6-character hex color
|
|
49
|
+
const isShort = Math.random() < 0.5;
|
|
50
|
+
let randomColor = Math.floor(Math.random() * (isShort ? 4095 : 16777215)).toString(16);
|
|
51
|
+
// Pad the color if necessary to make it 3 or 6 characters long
|
|
52
|
+
return isShort ? randomColor.padStart(3, '0') : randomColor.padStart(6, '0');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Add a click event listener to the link
|
|
56
|
+
document.getElementById('randomColorLink').addEventListener('click', function(event) {
|
|
57
|
+
event.preventDefault(); // Prevent the default action of the link
|
|
58
|
+
const randomColor = generateRandomColor(); // Generate a new random color
|
|
59
|
+
window.location.href = '/testpath/' + randomColor; // Navigate to the random color URL
|
|
60
|
+
});
|
|
61
|
+
</script>
|
|
62
|
+
</body>
|
|
63
|
+
</html>
|
|
64
|
+
`);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// Route to handle "/testfilters"
|
|
68
|
+
app.get("/testfilters", (req, res) => {
|
|
69
|
+
res.send(`
|
|
70
|
+
<html>
|
|
71
|
+
<body style="margin: 0; padding: 20px; font-family: Arial, sans-serif;">
|
|
72
|
+
<section id="section1" style="margin-bottom: 20px; border: 1px solid black; padding: 10px;">
|
|
73
|
+
<h2 style="color: silver; text-shadow: 1px 1px 1px black;">Primary Colors Section</h2>
|
|
74
|
+
<button style="background-color: red; color: white; padding: 10px;" onclick="changeColor('section1', 'red')">Red</button>
|
|
75
|
+
<button style="background-color: blue; color: white; padding: 10px;" onclick="changeColor('section1', 'blue')">Blue</button>
|
|
76
|
+
<button style="background-color: green; color: white; padding: 10px;" onclick="changeColor('section1', 'green')">Green</button>
|
|
77
|
+
<button style="background-color: lightgray; color: black; padding: 10px;" onclick="resetColor('section1')">Reset Color</button>
|
|
78
|
+
</section>
|
|
79
|
+
|
|
80
|
+
<section id="section2" style="margin-bottom: 20px; border: 1px solid black; padding: 10px;">
|
|
81
|
+
<h2 style="color: silver; text-shadow: 1px 1px 1px black;">Primary Colors Explorer</h2>
|
|
82
|
+
<button style="background-color: red; color: white; padding: 10px;" onclick="changeColor('section2', 'red')">Red</button>
|
|
83
|
+
<button style="background-color: blue; color: white; padding: 10px;" onclick="changeColor('section2', 'blue')">Blue</button>
|
|
84
|
+
<button style="background-color: green; color: white; padding: 10px;" onclick="changeColor('section2', 'green')">Green</button>
|
|
85
|
+
<button style="background-color: lightgray; color: black; padding: 10px;" onclick="resetColor('section2')">Reset Color</button>
|
|
86
|
+
</section>
|
|
87
|
+
|
|
88
|
+
<section id="section3" aria-label="Accessible Section" style="margin-bottom: 20px; border: 1px solid black; padding: 10px;">
|
|
89
|
+
<h2 style="color: silver; text-shadow: 1px 1px 1px black;">Primary Colors Test</h2>
|
|
90
|
+
<button style="background-color: red; color: white; padding: 10px;" onclick="changeColor('section3', 'red')">Red</button>
|
|
91
|
+
<button style="background-color: blue; color: white; padding: 10px;" onclick="changeColor('section3', 'blue')">Blue</button>
|
|
92
|
+
<button style="background-color: green; color: white; padding: 10px;" onclick="changeColor('section3', 'green')">Green</button>
|
|
93
|
+
<button style="background-color: lightgray; color: black; padding: 10px;" onclick="resetColor('section3')">Reset Color</button>
|
|
94
|
+
</section>
|
|
95
|
+
|
|
96
|
+
<section id="section4" style="margin-bottom: 20px; border: 1px solid black; padding: 10px;">
|
|
97
|
+
<h2 style="color: silver; text-shadow: 1px 1px 1px black;">Primary Colors Playground</h2>
|
|
98
|
+
<button style="background-color: red; color: white; padding: 10px;" onclick="changeColor('section4', 'red')">Red</button>
|
|
99
|
+
<button style="background-color: blue; color: white; padding: 10px;" onclick="changeColor('section4', 'blue')">Blue</button>
|
|
100
|
+
<button style="background-color: green; color: white; padding: 10px;" onclick="changeColor('section4', 'green')">Green</button>
|
|
101
|
+
<button style="background-color: lightgray; color: black; padding: 10px;" onclick="resetColor('section4')">Reset Color</button>
|
|
102
|
+
</section>
|
|
103
|
+
|
|
104
|
+
<script>
|
|
105
|
+
function changeColor(sectionId, color) {
|
|
106
|
+
document.getElementById(sectionId).style.backgroundColor = color;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function resetColor(sectionId) {
|
|
110
|
+
document.getElementById(sectionId).style.backgroundColor = "";
|
|
111
|
+
}
|
|
112
|
+
</script>
|
|
113
|
+
</body>
|
|
114
|
+
</html>
|
|
115
|
+
`);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
app.get("/iframe", (_req, res) => {
|
|
119
|
+
res.send(`
|
|
120
|
+
<html>
|
|
121
|
+
<body style="margin: 0; padding: 20px; font-family: Arial, sans-serif;">
|
|
122
|
+
<h1>Iframe playground</h1>
|
|
123
|
+
|
|
124
|
+
<section id="sectionA" style="margin-bottom: 20px; border: 2px solid #333; padding: 10px;">
|
|
125
|
+
<h2>Section A</h2>
|
|
126
|
+
<iframe
|
|
127
|
+
id="iframeA"
|
|
128
|
+
title="iframeA"
|
|
129
|
+
src="/iframe/a"
|
|
130
|
+
style="width: 400px; height: 200px; border: 3px solid #0066cc;"
|
|
131
|
+
></iframe>
|
|
132
|
+
</section>
|
|
133
|
+
|
|
134
|
+
<section id="sectionB" style="margin-bottom: 20px; border: 2px solid #333; padding: 10px;">
|
|
135
|
+
<h2>Section B</h2>
|
|
136
|
+
<iframe
|
|
137
|
+
id="iframeB"
|
|
138
|
+
title="iframeB"
|
|
139
|
+
src="/iframe/b"
|
|
140
|
+
style="width: 400px; height: 240px; border: 3px solid #cc6600;"
|
|
141
|
+
></iframe>
|
|
142
|
+
</section>
|
|
143
|
+
</body>
|
|
144
|
+
</html>
|
|
145
|
+
`);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
app.get("/iframe/a", (_req, res) => {
|
|
149
|
+
res.send(`
|
|
150
|
+
<html>
|
|
151
|
+
<body style="margin: 0; padding: 10px; font-family: Arial, sans-serif;">
|
|
152
|
+
<h3>Frame A</h3>
|
|
153
|
+
<button id="toggleA" data-testid="toggle-a" aria-pressed="false">Toggle A: Off</button>
|
|
154
|
+
<script>
|
|
155
|
+
const buttonA = document.getElementById('toggleA');
|
|
156
|
+
buttonA.addEventListener('click', () => {
|
|
157
|
+
const pressed = buttonA.getAttribute('aria-pressed') === 'true';
|
|
158
|
+
buttonA.setAttribute('aria-pressed', (!pressed).toString());
|
|
159
|
+
buttonA.textContent = pressed ? 'Toggle A: Off' : 'Toggle A: On';
|
|
160
|
+
});
|
|
161
|
+
</script>
|
|
162
|
+
</body>
|
|
163
|
+
</html>
|
|
164
|
+
`);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
app.get("/iframe/b", (_req, res) => {
|
|
168
|
+
res.send(`
|
|
169
|
+
<html>
|
|
170
|
+
<body style="margin: 0; padding: 10px; font-family: Arial, sans-serif;">
|
|
171
|
+
<h3>Frame B</h3>
|
|
172
|
+
<button id="toggleB" data-testid="toggle-b" aria-pressed="false">Toggle B: Off</button>
|
|
173
|
+
|
|
174
|
+
<iframe
|
|
175
|
+
id="iframeC"
|
|
176
|
+
title="iframeC"
|
|
177
|
+
src="/iframe/c"
|
|
178
|
+
style="display: block; margin-top: 12px; width: 320px; height: 160px; border: 3px solid #009933;"
|
|
179
|
+
></iframe>
|
|
180
|
+
|
|
181
|
+
<script>
|
|
182
|
+
const buttonB = document.getElementById('toggleB');
|
|
183
|
+
buttonB.addEventListener('click', () => {
|
|
184
|
+
const pressed = buttonB.getAttribute('aria-pressed') === 'true';
|
|
185
|
+
buttonB.setAttribute('aria-pressed', (!pressed).toString());
|
|
186
|
+
buttonB.textContent = pressed ? 'Toggle B: Off' : 'Toggle B: On';
|
|
187
|
+
});
|
|
188
|
+
</script>
|
|
189
|
+
</body>
|
|
190
|
+
</html>
|
|
191
|
+
`);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
app.get("/iframe/c", (_req, res) => {
|
|
195
|
+
res.send(`
|
|
196
|
+
<html>
|
|
197
|
+
<body style="margin: 0; padding: 10px; font-family: Arial, sans-serif;">
|
|
198
|
+
<h3>Frame C</h3>
|
|
199
|
+
<button id="toggleC" data-testid="toggle-c" aria-pressed="false">Toggle C: Off</button>
|
|
200
|
+
<script>
|
|
201
|
+
const buttonC = document.getElementById('toggleC');
|
|
202
|
+
buttonC.addEventListener('click', () => {
|
|
203
|
+
const pressed = buttonC.getAttribute('aria-pressed') === 'true';
|
|
204
|
+
buttonC.setAttribute('aria-pressed', (!pressed).toString());
|
|
205
|
+
buttonC.textContent = pressed ? 'Toggle C: Off' : 'Toggle C: On';
|
|
206
|
+
});
|
|
207
|
+
</script>
|
|
208
|
+
</body>
|
|
209
|
+
</html>
|
|
210
|
+
`);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
// Start the server
|
|
214
|
+
app.listen(port, () => {
|
|
215
|
+
console.log(`Server running at http://localhost:${port}/`);
|
|
216
|
+
});
|