Add Playwright component test quarantine flow
This commit is contained in:
@@ -486,6 +486,7 @@ jobs:
|
||||
install_dependencies
|
||||
ulimit -n 16384 || true
|
||||
if [[ "$MATRIX_SUITE" == "core" ]]; then
|
||||
PLAYWRIGHT_ARTIFACT_NAMESPACE="${PLAYWRIGHT_ARTIFACT_NAMESPACE}-ct" npm run test:ct -- --project="$MATRIX_PROJECT"
|
||||
npx playwright test --grep @smoke --project="$MATRIX_PROJECT"
|
||||
npm run test:e2e:pr -- --core-only --project="$MATRIX_PROJECT"
|
||||
else
|
||||
|
||||
Generated
+224
@@ -70,6 +70,7 @@
|
||||
"@creativebulma/bulma-divider": "^1.1.0",
|
||||
"@eslint/js": "^10.0.1",
|
||||
"@event-calendar/core": "^4.1.0",
|
||||
"@playwright/experimental-ct-vue": "^1.58.2",
|
||||
"@playwright/test": "^1.58.2",
|
||||
"@types/event-calendar__core": "^3.7.0",
|
||||
"@vitejs/plugin-vue": "^6.0.5",
|
||||
@@ -4219,6 +4220,229 @@
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/@playwright/experimental-ct-core": {
|
||||
"version": "1.58.2",
|
||||
"resolved": "https://registry.npmjs.org/@playwright/experimental-ct-core/-/experimental-ct-core-1.58.2.tgz",
|
||||
"integrity": "sha512-Imif9ggQp6YIblHAX6MvJuqDFrCGHYspoibxLP3+1soXp+1wBNuuSRajv0VWXzFbh//4l29I+xy2tpTgej0vEA==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"playwright": "1.58.2",
|
||||
"playwright-core": "1.58.2",
|
||||
"vite": "^6.4.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@playwright/experimental-ct-core/node_modules/fsevents": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@playwright/experimental-ct-core/node_modules/vite": {
|
||||
"version": "6.4.3",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-6.4.3.tgz",
|
||||
"integrity": "sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.25.0",
|
||||
"fdir": "^6.4.4",
|
||||
"picomatch": "^4.0.2",
|
||||
"postcss": "^8.5.3",
|
||||
"rollup": "^4.34.9",
|
||||
"tinyglobby": "^0.2.13"
|
||||
},
|
||||
"bin": {
|
||||
"vite": "bin/vite.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.0.0 || ^20.0.0 || >=22.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/vitejs/vite?sponsor=1"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "~2.3.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
|
||||
"jiti": ">=1.21.0",
|
||||
"less": "*",
|
||||
"lightningcss": "^1.21.0",
|
||||
"sass": "*",
|
||||
"sass-embedded": "*",
|
||||
"stylus": "*",
|
||||
"sugarss": "*",
|
||||
"terser": "^5.16.0",
|
||||
"tsx": "^4.8.1",
|
||||
"yaml": "^2.4.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/node": {
|
||||
"optional": true
|
||||
},
|
||||
"jiti": {
|
||||
"optional": true
|
||||
},
|
||||
"less": {
|
||||
"optional": true
|
||||
},
|
||||
"lightningcss": {
|
||||
"optional": true
|
||||
},
|
||||
"sass": {
|
||||
"optional": true
|
||||
},
|
||||
"sass-embedded": {
|
||||
"optional": true
|
||||
},
|
||||
"stylus": {
|
||||
"optional": true
|
||||
},
|
||||
"sugarss": {
|
||||
"optional": true
|
||||
},
|
||||
"terser": {
|
||||
"optional": true
|
||||
},
|
||||
"tsx": {
|
||||
"optional": true
|
||||
},
|
||||
"yaml": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@playwright/experimental-ct-vue": {
|
||||
"version": "1.58.2",
|
||||
"resolved": "https://registry.npmjs.org/@playwright/experimental-ct-vue/-/experimental-ct-vue-1.58.2.tgz",
|
||||
"integrity": "sha512-joLgyXZOV7odSY3PrEkzNRqz6rdaf7eoyTus4deSAAD6u5qvWYgDZl2mDJYiivcE+jDns9SGiZQlkMmyDcjQpg==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@playwright/experimental-ct-core": "1.58.2",
|
||||
"@vitejs/plugin-vue": "^5.2.0"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@playwright/experimental-ct-vue/node_modules/@vitejs/plugin-vue": {
|
||||
"version": "5.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz",
|
||||
"integrity": "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "^18.0.0 || >=20.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vite": "^5.0.0 || ^6.0.0",
|
||||
"vue": "^3.2.25"
|
||||
}
|
||||
},
|
||||
"node_modules/@playwright/experimental-ct-vue/node_modules/fsevents": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@playwright/experimental-ct-vue/node_modules/vite": {
|
||||
"version": "6.4.3",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-6.4.3.tgz",
|
||||
"integrity": "sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.25.0",
|
||||
"fdir": "^6.4.4",
|
||||
"picomatch": "^4.0.2",
|
||||
"postcss": "^8.5.3",
|
||||
"rollup": "^4.34.9",
|
||||
"tinyglobby": "^0.2.13"
|
||||
},
|
||||
"bin": {
|
||||
"vite": "bin/vite.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.0.0 || ^20.0.0 || >=22.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/vitejs/vite?sponsor=1"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "~2.3.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
|
||||
"jiti": ">=1.21.0",
|
||||
"less": "*",
|
||||
"lightningcss": "^1.21.0",
|
||||
"sass": "*",
|
||||
"sass-embedded": "*",
|
||||
"stylus": "*",
|
||||
"sugarss": "*",
|
||||
"terser": "^5.16.0",
|
||||
"tsx": "^4.8.1",
|
||||
"yaml": "^2.4.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/node": {
|
||||
"optional": true
|
||||
},
|
||||
"jiti": {
|
||||
"optional": true
|
||||
},
|
||||
"less": {
|
||||
"optional": true
|
||||
},
|
||||
"lightningcss": {
|
||||
"optional": true
|
||||
},
|
||||
"sass": {
|
||||
"optional": true
|
||||
},
|
||||
"sass-embedded": {
|
||||
"optional": true
|
||||
},
|
||||
"stylus": {
|
||||
"optional": true
|
||||
},
|
||||
"sugarss": {
|
||||
"optional": true
|
||||
},
|
||||
"terser": {
|
||||
"optional": true
|
||||
},
|
||||
"tsx": {
|
||||
"optional": true
|
||||
},
|
||||
"yaml": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@playwright/test": {
|
||||
"version": "1.58.2",
|
||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.58.2.tgz",
|
||||
|
||||
+4
-1
@@ -47,11 +47,13 @@
|
||||
"test:e2e:live:public": "playwright test --config=playwright.live.config.ts --grep @public-live",
|
||||
"test:e2e:live:roles": "playwright test --config=playwright.live.config.ts --grep @role-live",
|
||||
"test:e2e:release": "npm run test:e2e:prod && npm run test:e2e:live",
|
||||
"test:ct": "playwright test --config=playwright.ct.config.ts",
|
||||
"test:ct:pr": "playwright test --config=playwright.ct.config.ts --project=chromium-desktop",
|
||||
"release:verify-upload": "node scripts/release/verify-upload.mjs",
|
||||
"release:update-server-version": "node scripts/release/update-server-version.mjs",
|
||||
"release:upload:lftp": "bash scripts/release/upload-dist-lftp.sh",
|
||||
"test:e2e:pos-mobile-live": "node -e \"const { spawnSync } = require('child_process'); const result = spawnSync('npx', ['playwright', 'test', 'tests/e2e/adminModulePosMobileOrderFlow.spec.ts', '--project=chromium-mobile'], { stdio: 'inherit', shell: true, env: { ...process.env, PLAYWRIGHT_LIVE: '1' } }); process.exit(result.status ?? 1);\"",
|
||||
"test:all": "npm run test:unit && npm run test:e2e:pr",
|
||||
"test:all": "npm run test:unit && npm run test:ct:pr && npm run test:e2e:pr",
|
||||
"twa:build": "bubblewrap build",
|
||||
"twa:update": "bubblewrap update",
|
||||
"mobile:sync": "npm run build && npx cap sync",
|
||||
@@ -128,6 +130,7 @@
|
||||
"@creativebulma/bulma-divider": "^1.1.0",
|
||||
"@eslint/js": "^10.0.1",
|
||||
"@event-calendar/core": "^4.1.0",
|
||||
"@playwright/experimental-ct-vue": "^1.58.2",
|
||||
"@playwright/test": "^1.58.2",
|
||||
"@types/event-calendar__core": "^3.7.0",
|
||||
"@vitejs/plugin-vue": "^6.0.5",
|
||||
|
||||
@@ -40,7 +40,7 @@ function buildProject(name: string, browserName: "chromium" | "firefox" | "webki
|
||||
|
||||
export default defineConfig({
|
||||
testDir: "./tests/e2e",
|
||||
testIgnore: ["**/release/**"],
|
||||
testIgnore: ["**/release/**", "**/quarantine/**"],
|
||||
snapshotPathTemplate: "{snapshotDir}/{testFileDir}/{testFileName}-snapshots/{arg}{-projectName}-win32{ext}",
|
||||
timeout: 60_000,
|
||||
fullyParallel: true,
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { defineConfig, devices } from "@playwright/experimental-ct-vue";
|
||||
|
||||
const projectRoot = fileURLToPath(new URL(".", import.meta.url));
|
||||
const artifactNamespace = (process.env.PLAYWRIGHT_ARTIFACT_NAMESPACE || "ct").trim();
|
||||
const artifactRoot = path.join("output", "playwright", artifactNamespace);
|
||||
const reporterMode = (process.env.PLAYWRIGHT_REPORTER_MODE || "").trim();
|
||||
const reporter =
|
||||
reporterMode === "line-html"
|
||||
? [["line"], ["html", { open: "never", outputFolder: path.join(artifactRoot, "report") }]]
|
||||
: [["list"], ["html", { open: "never", outputFolder: path.join(artifactRoot, "report") }]];
|
||||
const configuredWorkers = Number(process.env.PLAYWRIGHT_WORKERS || 2);
|
||||
const workers = Number.isFinite(configuredWorkers) && configuredWorkers > 0 ? configuredWorkers : 2;
|
||||
|
||||
export default defineConfig({
|
||||
testDir: "./tests/ct",
|
||||
timeout: 45_000,
|
||||
fullyParallel: true,
|
||||
forbidOnly: !!process.env.CI,
|
||||
retries: process.env.CI ? 1 : 0,
|
||||
workers,
|
||||
reporter,
|
||||
outputDir: path.join(artifactRoot, "test-results"),
|
||||
use: {
|
||||
trace: "retain-on-failure",
|
||||
screenshot: "only-on-failure",
|
||||
video: "retain-on-failure",
|
||||
ctViteConfig: {
|
||||
resolve: {
|
||||
alias: {
|
||||
"@": path.resolve(projectRoot, "src"),
|
||||
},
|
||||
preserveSymlinks: true,
|
||||
dedupe: ["vue", "vue-router", "vue-i18n", "@vueuse/core", "@vueuse/head", "@unhead/vue"],
|
||||
},
|
||||
},
|
||||
},
|
||||
projects: [
|
||||
{
|
||||
name: "chromium-desktop",
|
||||
use: {
|
||||
...devices["Desktop Chrome"],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "chromium-mobile",
|
||||
use: {
|
||||
...devices["Pixel 5"],
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -0,0 +1,12 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Pleno Component Test</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="./index.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,25 @@
|
||||
import { beforeMount } from "@playwright/experimental-ct-vue/hooks";
|
||||
import Buefy from "buefy";
|
||||
import "bulma/css/bulma.min.css";
|
||||
import "buefy/dist/css/buefy.css";
|
||||
|
||||
import i18n from "@/i18n";
|
||||
|
||||
type PlenoHooksConfig = {
|
||||
locale?: string;
|
||||
};
|
||||
|
||||
beforeMount(({ app, hooksConfig }) => {
|
||||
const config = (hooksConfig || {}) as PlenoHooksConfig;
|
||||
const locale = config.locale || "en";
|
||||
const globalLocale = i18n.global.locale as unknown;
|
||||
|
||||
if (globalLocale && typeof globalLocale === "object" && "value" in globalLocale) {
|
||||
(globalLocale as { value: string }).value = locale;
|
||||
} else {
|
||||
(i18n.global as unknown as { locale: string }).locale = locale;
|
||||
}
|
||||
|
||||
app.use(i18n);
|
||||
app.use(Buefy);
|
||||
});
|
||||
@@ -2,7 +2,13 @@ import { execFile, spawn } from "node:child_process";
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { promisify } from "node:util";
|
||||
import { chromiumProjects, fallbackChangePatterns, prGrep, smokeGrep, sourceMappings } from "./playwright-pr-mapping.mjs";
|
||||
import {
|
||||
chromiumProjects,
|
||||
fallbackChangePatterns,
|
||||
prGrep,
|
||||
smokeGrep,
|
||||
sourceMappings,
|
||||
} from "./playwright-pr-mapping.mjs";
|
||||
|
||||
const execFileAsync = promisify(execFile);
|
||||
const workingDirectory = process.cwd();
|
||||
@@ -245,7 +251,7 @@ function addSpec(selection, spec, projects) {
|
||||
}
|
||||
|
||||
function isE2eSpec(file) {
|
||||
return /^tests\/e2e\/.+\.spec\.(?:js|ts)$/u.test(file);
|
||||
return /^tests\/e2e\/(?!quarantine\/).+\.spec\.(?:js|ts)$/u.test(file);
|
||||
}
|
||||
|
||||
function shouldFallback(file) {
|
||||
@@ -361,7 +367,9 @@ async function runChangedSelection(selection) {
|
||||
|
||||
if (selection.fallback) {
|
||||
console.log(
|
||||
`[playwright-pr] Falling back to broader ${smokeGrep} coverage because these changed files were unmapped: ${selection.unmappedFiles.join(", ")}`
|
||||
`[playwright-pr] Falling back to broader ${smokeGrep} coverage because these changed files were unmapped: ${selection.unmappedFiles.join(
|
||||
", "
|
||||
)}`
|
||||
);
|
||||
for (const project of projects) {
|
||||
const code = await runPlaywright({
|
||||
@@ -386,7 +394,9 @@ async function runChangedSelection(selection) {
|
||||
|
||||
if (selection.skippedDirectSpecFiles.length > 0) {
|
||||
console.log(
|
||||
`[playwright-pr] Source mappings selected changed-area specs; direct E2E file edits are covered by mapped/core gates: ${selection.skippedDirectSpecFiles.join(", ")}`
|
||||
`[playwright-pr] Source mappings selected changed-area specs; direct E2E file edits are covered by mapped/core gates: ${selection.skippedDirectSpecFiles.join(
|
||||
", "
|
||||
)}`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -466,7 +476,9 @@ async function main() {
|
||||
|
||||
const changed = await getChangedFiles();
|
||||
if (changed.unavailable) {
|
||||
console.log(`[playwright-pr] Changed-area diff unavailable for ${changed.source}; skipping changed-area selection.`);
|
||||
console.log(
|
||||
`[playwright-pr] Changed-area diff unavailable for ${changed.source}; skipping changed-area selection.`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import { useI18n } from "vue-i18n";
|
||||
|
||||
import { SessionUser } from "@/components/session/token/SessionUser.vue";
|
||||
import { formatLocalDateOnly } from "@/services/dateOnly.js";
|
||||
import { buildRelativeDateShortcuts } from "@/services/relativeDateShortcuts.js";
|
||||
import { buildRelativeDateShortcuts, splitRelativeDateShortcuts } from "@/services/relativeDateShortcuts.js";
|
||||
import BuefyDateField from "@/components/forms/BuefyDateField.vue";
|
||||
import BuefyMonthField from "@/components/forms/BuefyMonthField.vue";
|
||||
|
||||
@@ -300,21 +300,17 @@ const shortcuts = computed<Shortcut[]>(() => (
|
||||
));
|
||||
|
||||
const relativeShortcutBuckets = computed(() => {
|
||||
const primaryShortcutIds = new Set(props.primaryShortcutIds);
|
||||
const allShortcuts = buildRelativeDateShortcuts().filter((shortcut) => enabledShortcutIds.value.includes(shortcut.key));
|
||||
const mapShortcut = (shortcut) => ({
|
||||
id: shortcut.key,
|
||||
label: shortcut.label,
|
||||
getRange: shortcut.getRange,
|
||||
});
|
||||
const { primary, other } = splitRelativeDateShortcuts(allShortcuts, props.primaryShortcutIds);
|
||||
|
||||
return {
|
||||
primary: allShortcuts
|
||||
.filter((shortcut) => primaryShortcutIds.has(shortcut.key))
|
||||
.map(mapShortcut),
|
||||
other: allShortcuts
|
||||
.filter((shortcut) => !primaryShortcutIds.has(shortcut.key))
|
||||
.map(mapShortcut),
|
||||
primary: primary.map(mapShortcut),
|
||||
other: other.map(mapShortcut),
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
import DatePeriodSelector from "@/components/displays/buttons/DatePeriodSelector.vue";
|
||||
|
||||
import { expect, test } from "./fixtures/plenoCt";
|
||||
|
||||
const selection = {
|
||||
startDate: new Date("2026-05-01T00:00:00.000Z"),
|
||||
endDate: new Date("2026-05-04T23:59:59.999Z"),
|
||||
};
|
||||
|
||||
const mountDatePeriodSelector = async (mount, props = {}) =>
|
||||
mount(DatePeriodSelector, {
|
||||
props: {
|
||||
selection,
|
||||
allowEmptySelection: true,
|
||||
visibility: {
|
||||
showMonthSelector: true,
|
||||
showYearSelector: true,
|
||||
showUpdateButton: false,
|
||||
},
|
||||
onSelectionChange: () => {},
|
||||
...props,
|
||||
},
|
||||
hooksConfig: {
|
||||
locale: "en",
|
||||
},
|
||||
});
|
||||
|
||||
test.describe("DatePeriodSelector component", () => {
|
||||
test("desktop other month action opens an inline month picker modal", async ({ mount, page }, testInfo) => {
|
||||
test.skip(testInfo.project.name.includes("mobile"), "Desktop shortcut buttons are replaced by the mobile select.");
|
||||
|
||||
await page.clock.setFixedTime(new Date("2026-05-04T10:00:00.000Z"));
|
||||
|
||||
const component = await mountDatePeriodSelector(mount);
|
||||
|
||||
await expect(component.getByTestId("date-period-shortcut-anytime")).toContainText(/anytime/i);
|
||||
await component.getByTestId("date-period-other-dropdown-trigger").click();
|
||||
|
||||
const otherMonthOption = component.getByTestId("date-period-other-other_month");
|
||||
await expect(otherMonthOption).toBeVisible();
|
||||
await expect(otherMonthOption).toContainText(/other month/i);
|
||||
|
||||
await otherMonthOption.click();
|
||||
|
||||
await expect(component.getByTestId("date-period-other-month-modal")).toBeVisible();
|
||||
await expect(component.getByTestId("date-period-other-month-picker")).toBeVisible();
|
||||
});
|
||||
|
||||
test("mobile shortcuts keep primary and other actions reachable without route boot", async ({ mount, page }) => {
|
||||
await page.setViewportSize({ width: 390, height: 844 });
|
||||
await page.clock.setFixedTime(new Date("2026-05-04T10:00:00.000Z"));
|
||||
|
||||
const component = await mountDatePeriodSelector(mount);
|
||||
|
||||
const shortcutSelect = component.getByTestId("date-period-shortcuts");
|
||||
await expect(shortcutSelect.locator("option").filter({ hasText: /anytime/i })).toHaveCount(1);
|
||||
|
||||
await component.getByTestId("date-period-other-dropdown-trigger").click();
|
||||
await expect(component.getByTestId("date-period-other-other_month")).toBeVisible();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,14 @@
|
||||
import { expect, test as base } from "@playwright/experimental-ct-vue";
|
||||
|
||||
export const test = base.extend({
|
||||
page: async ({ page }, use) => {
|
||||
await page.addStyleTag({
|
||||
content:
|
||||
"#__vue-devtools-container__, .vue-devtools__anchor-btn, .vue-devtools__panel-content { display: none !important; visibility: hidden !important; pointer-events: none !important; }",
|
||||
});
|
||||
|
||||
await use(page);
|
||||
},
|
||||
});
|
||||
|
||||
export { expect };
|
||||
@@ -0,0 +1,10 @@
|
||||
[
|
||||
{
|
||||
"originalPath": "tests/e2e/date-period-selector.smoke.spec.js",
|
||||
"quarantinePath": "tests/e2e/quarantine/component-harness/date-period-selector.smoke.spec.js",
|
||||
"cluster": "component-harness",
|
||||
"reason": "The active spec exercises a dedicated DatePeriodSelector harness rather than route, auth, or backend integration behavior.",
|
||||
"replacementCoverage": ["tests/unit/date-period-selector.spec.js", "tests/ct/date-period-selector.ct.spec.ts"],
|
||||
"removalCriteria": "Remove after Playwright CT is installable in CI and the replacement CT plus unit coverage pass."
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,43 @@
|
||||
import { existsSync, readFileSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
const root = process.cwd();
|
||||
const quarantineManifestPath = join(root, "tests/e2e/quarantine/quarantine.json");
|
||||
|
||||
function readQuarantineManifest() {
|
||||
return JSON.parse(readFileSync(quarantineManifestPath, "utf8"));
|
||||
}
|
||||
|
||||
describe("Playwright E2E quarantine", () => {
|
||||
it("keeps quarantined specs out of the active E2E tree while replacements are introduced", () => {
|
||||
const manifest = readQuarantineManifest();
|
||||
|
||||
expect(Array.isArray(manifest)).toBe(true);
|
||||
expect(manifest.length).toBeGreaterThan(0);
|
||||
|
||||
for (const entry of manifest) {
|
||||
expect(entry.originalPath).toMatch(/^tests\/e2e\/.+\.spec\.(?:js|ts)$/u);
|
||||
expect(entry.quarantinePath).toMatch(/^tests\/e2e\/quarantine\/.+\.spec\.(?:js|ts)$/u);
|
||||
expect(entry.reason).toEqual(expect.any(String));
|
||||
expect(entry.removalCriteria).toEqual(expect.any(String));
|
||||
expect(entry.replacementCoverage).toEqual(expect.arrayContaining([expect.stringMatching(/^tests\//u)]));
|
||||
expect(existsSync(join(root, entry.originalPath))).toBe(false);
|
||||
expect(existsSync(join(root, entry.quarantinePath))).toBe(true);
|
||||
}
|
||||
});
|
||||
|
||||
it("ignores quarantined specs in the active Playwright E2E config", () => {
|
||||
const source = readFileSync(join(root, "playwright.config.ts"), "utf8");
|
||||
|
||||
expect(source).toContain('"**/quarantine/**"');
|
||||
});
|
||||
|
||||
it("keeps component coverage wired into local and CI gates", () => {
|
||||
const packageJson = JSON.parse(readFileSync(join(root, "package.json"), "utf8"));
|
||||
const workflowSource = readFileSync(join(root, ".github/workflows/tests.yml"), "utf8");
|
||||
|
||||
expect(packageJson.scripts["test:all"]).toContain("npm run test:ct:pr");
|
||||
expect(workflowSource).toContain('npm run test:ct -- --project="$MATRIX_PROJECT"');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user