160 lines
5.6 KiB
JavaScript
160 lines
5.6 KiB
JavaScript
import { execFileSync } from "node:child_process";
|
||
import { readFileSync } from "node:fs";
|
||
import fs from "node:fs/promises";
|
||
import { readdirSync } from "node:fs";
|
||
import { tmpdir } from "node:os";
|
||
import { join } from "node:path";
|
||
import { afterEach, describe, expect, it } from "vitest";
|
||
import {
|
||
browserEngines,
|
||
classifyTest,
|
||
deviceClasses,
|
||
fullSuiteProjects,
|
||
getLegacyTestListPath,
|
||
getPrimaryTestListPath,
|
||
ownedFilesByRole,
|
||
parseListedTests,
|
||
requireMatchingTests,
|
||
roles,
|
||
titleRules,
|
||
writeTestList,
|
||
} from "../../scripts/run-playwright-full-slice.mjs";
|
||
|
||
const root = process.cwd();
|
||
const playwrightCliPath = join(root, "node_modules", "@playwright", "test", "cli.js");
|
||
const titleRuleFiles = new Set(titleRules.map((rule) => rule.file));
|
||
const e2eSpecFiles = readdirSync(join(root, "tests/e2e"))
|
||
.filter((file) => /\.spec\.(?:js|ts)$/u.test(file))
|
||
.sort();
|
||
const generatedTestListPaths = [];
|
||
const generatedTestListDirectories = [];
|
||
|
||
describe("Playwright full-slice ownership", () => {
|
||
afterEach(async () => {
|
||
await Promise.all(generatedTestListPaths.splice(0).map((filePath) => fs.rm(filePath, { force: true })));
|
||
await Promise.all(
|
||
generatedTestListDirectories.splice(0).map((directory) => fs.rm(directory, { recursive: true, force: true }))
|
||
);
|
||
delete process.env.PLAYWRIGHT_TEST_LIST_DIR;
|
||
});
|
||
|
||
it("keeps full-suite slices ordered by browser, device, then role", () => {
|
||
expect(browserEngines).toEqual(["chromium", "webkit", "firefox"]);
|
||
expect(deviceClasses).toEqual(["mobile", "desktop", "tablet"]);
|
||
expect(roles).toEqual(["superuser", "admin", "customer", "subuser"]);
|
||
expect(fullSuiteProjects).toEqual([
|
||
"chromium-mobile",
|
||
"chromium-desktop",
|
||
"chromium-tablet",
|
||
"webkit-mobile",
|
||
"webkit-desktop",
|
||
"webkit-tablet",
|
||
"firefox-mobile",
|
||
"firefox-desktop",
|
||
"firefox-tablet",
|
||
]);
|
||
});
|
||
|
||
it("assigns every top-level e2e spec to one role or a title rule", () => {
|
||
const directOwners = new Map();
|
||
|
||
for (const [role, files] of Object.entries(ownedFilesByRole)) {
|
||
for (const file of files) {
|
||
if (directOwners.has(file)) {
|
||
throw new Error(`${file} is owned by both ${directOwners.get(file)} and ${role}.`);
|
||
}
|
||
|
||
directOwners.set(file, role);
|
||
}
|
||
}
|
||
|
||
const coveredFiles = new Set([...directOwners.keys(), ...titleRuleFiles]);
|
||
const uncoveredFiles = e2eSpecFiles.filter((file) => !coveredFiles.has(file));
|
||
|
||
expect(uncoveredFiles).toEqual([]);
|
||
});
|
||
|
||
it("keeps shared and infrastructure specs in their intended shards", () => {
|
||
expect(ownedFilesByRole.admin).toEqual(expect.arrayContaining(["default-mobile-redirect.spec.ts"]));
|
||
expect(ownedFilesByRole.superuser).toEqual(
|
||
expect.arrayContaining([
|
||
"errorReports.spec.ts",
|
||
"failover-config.source.spec.ts",
|
||
"release-manager.spec.js",
|
||
"session-bootstrap.spec.ts",
|
||
"superuser-users.spec.ts",
|
||
])
|
||
);
|
||
expect(ownedFilesByRole.customer).toEqual(
|
||
expect.arrayContaining([
|
||
"guest-book-wash-mobile.spec.ts",
|
||
"i18n-v2-integrity.spec.ts",
|
||
"release-bootstrap.spec.js",
|
||
"release-channel-switched.spec.js",
|
||
"release-channel-unavailable.spec.js",
|
||
"release-update-widget.spec.js",
|
||
"session-release-runtime.spec.ts",
|
||
])
|
||
);
|
||
});
|
||
|
||
it("classifies every listed test before role filtering", () => {
|
||
const listOutput = execFileSync(
|
||
process.execPath,
|
||
[playwrightCliPath, "test", "--list", "--reporter=list", "--project=chromium-desktop"],
|
||
{
|
||
cwd: root,
|
||
encoding: "utf8",
|
||
maxBuffer: 64 * 1024 * 1024,
|
||
}
|
||
);
|
||
const classificationErrors = [];
|
||
|
||
for (const testEntry of parseListedTests(listOutput)) {
|
||
try {
|
||
classifyTest(testEntry);
|
||
} catch (error) {
|
||
classificationErrors.push(error.message);
|
||
}
|
||
}
|
||
|
||
expect(classificationErrors).toEqual([]);
|
||
}, 15_000);
|
||
|
||
it("writes project-role test lists and a legacy compatibility copy", async () => {
|
||
const testListDirectory = await fs.mkdtemp(join(tmpdir(), "playwright-test-lists-"));
|
||
process.env.PLAYWRIGHT_TEST_LIST_DIR = testListDirectory;
|
||
generatedTestListDirectories.push(testListDirectory);
|
||
|
||
const matchingTests = [
|
||
{
|
||
listLine: "[webkit-tablet] › tests/e2e/admin-pos-orders.spec.ts:10:1 › admin order list",
|
||
},
|
||
];
|
||
const primaryPath = getPrimaryTestListPath("webkit-tablet", "admin");
|
||
const legacyPath = getLegacyTestListPath("admin", "webkit-tablet");
|
||
generatedTestListPaths.push(primaryPath, legacyPath);
|
||
|
||
await expect(writeTestList("admin", "webkit-tablet", matchingTests)).resolves.toBe(primaryPath);
|
||
|
||
expect(readFileSync(primaryPath, "utf8")).toBe(`${matchingTests[0].listLine}\n`);
|
||
expect(readFileSync(legacyPath, "utf8")).toBe(`${matchingTests[0].listLine}\n`);
|
||
});
|
||
|
||
it("fails closed when a required project-role slice resolves zero tests", () => {
|
||
expect(() => requireMatchingTests("customer", "webkit-mobile", [])).toThrow(
|
||
"Full Playwright slice resolved zero customer tests for webkit-mobile."
|
||
);
|
||
expect(requireMatchingTests("customer", "webkit-mobile", [{ listLine: "one test" }])).toHaveLength(1);
|
||
});
|
||
});
|
||
|
||
describe("Playwright full-suite project order", () => {
|
||
it("keeps playwright.config projects ordered by browser engine and device class", () => {
|
||
const source = readFileSync(join(root, "playwright.config.ts"), "utf8");
|
||
const projectNames = [...source.matchAll(/buildProject\("([^"]+)"/gu)].map((match) => match[1]);
|
||
|
||
expect(projectNames).toEqual(fullSuiteProjects);
|
||
});
|
||
});
|