@stencil/playwright
Version:
Testing adapter to use Playwright with Stencil
151 lines (146 loc) • 5.4 kB
JavaScript
// src/wizard.ts
import { access, readdir, readFile, writeFile } from "node:fs/promises";
import { join } from "node:path";
async function fileExists(path) {
try {
await access(path);
return true;
} catch {
return false;
}
}
function detectOutputTargets(config) {
const outputs = config.outputTargets;
const www = outputs.some((o) => o.type === "www");
const loaderBundle = outputs.some((o) => o.type === "loader-bundle");
return { www, loaderBundle };
}
var PLAYWRIGHT_CONFIG_TEMPLATE = `import { expect } from '@playwright/test';
import { matchers, createConfig } from '@stencil/playwright';
// Add custom Stencil matchers to Playwright assertions
expect.extend(matchers);
export default createConfig({
// Overwrite Playwright config options here
});
`;
function e2eSpecTemplate(tagName) {
return `import { expect } from '@playwright/test';
import { test } from '@stencil/playwright';
test.describe('${tagName}', () => {
test('renders', async ({ page }) => {
await page.setContent('<${tagName}></${tagName}>');
const el = page.locator('${tagName}');
await expect(el).toBeAttached();
});
});
`;
}
async function ensureDisposableLib(rootDir) {
const tsconfigPath = join(rootDir, "tsconfig.json");
if (!await fileExists(tsconfigPath)) return;
try {
const tsconfig = JSON.parse(await readFile(tsconfigPath, "utf8"));
const lib = tsconfig.compilerOptions?.lib ?? [];
if (lib.some((l) => l.toLowerCase() === "esnext.disposable")) return;
tsconfig.compilerOptions ??= {};
tsconfig.compilerOptions.lib = [...lib, "ESNext.Disposable"];
await writeFile(tsconfigPath, JSON.stringify(tsconfig, null, 2) + "\n", "utf8");
} catch {
}
}
async function ensureOutputTarget(context) {
const { config, prompts } = context;
const { www, loaderBundle } = detectOutputTargets(config);
if (www || loaderBundle) return;
const stencilConfigPath = join(config.rootDir, "stencil.config.ts");
if (!await fileExists(stencilConfigPath)) {
return;
}
const addWww = await prompts.confirm({
message: 'No "www" or "loader-bundle" output target found. Add a "www" output target now?',
initialValue: true
});
if (!prompts.isCancel(addWww) && addWww) {
const editor = await context.openStencilConfig();
editor.addOutputTarget("{ type: 'www', serviceWorker: null }");
await editor.save();
return;
}
prompts.log.warn(
'No "www" or "loader-bundle" output target configured - Playwright tests may not have anything to run against.'
);
}
async function updatePackageJsonScripts(rootDir) {
const pkgPath = join(rootDir, "package.json");
const pkg = JSON.parse(await readFile(pkgPath, "utf8"));
pkg.scripts ??= {};
pkg.scripts["test:e2e"] ??= "playwright test";
await writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
}
async function generateExampleTests(rootDir) {
const componentsDir = join(rootDir, "src", "components");
const entries = await readdir(componentsDir, { withFileTypes: true }).catch(() => []);
for (const entry of entries) {
if (!entry.isDirectory()) continue;
const componentFile = join(componentsDir, entry.name, `${entry.name}.tsx`);
if (!await fileExists(componentFile)) continue;
const source = await readFile(componentFile, "utf8");
if (!source.includes("@Component")) continue;
const tagMatch = source.match(/tag:\s*['"]([^'"]+)['"]/);
const tagName = tagMatch?.[1] ?? entry.name;
const specFile = join(componentsDir, entry.name, `${tagName}.e2e.ts`);
if (await fileExists(specFile)) continue;
await writeFile(specFile, e2eSpecTemplate(tagName), "utf8");
}
}
var wizard = {
init: {
id: "@stencil/playwright",
displayName: "Playwright",
description: "E2E testing",
async run(context) {
const { config, isNewProject, prompts, nypm } = context;
const { intro, outro, confirm, isCancel, cancel, spinner } = prompts;
const rootDir = config.rootDir;
intro("Playwright - E2E testing for Stencil");
const playwrightConfigPath = join(rootDir, "playwright.config.ts");
if (!isNewProject && await fileExists(playwrightConfigPath)) {
const overwrite = await confirm({
message: "playwright.config.ts already exists. Overwrite it?",
initialValue: false
});
if (isCancel(overwrite) || !overwrite) {
cancel("Skipping Playwright setup - existing config kept.");
return;
}
}
const s = spinner();
s.start("Installing dependencies");
await nypm.addDependency(["@playwright/test"], { cwd: rootDir, dev: true });
s.stop("Dependencies installed");
await writeFile(playwrightConfigPath, PLAYWRIGHT_CONFIG_TEMPLATE, "utf8");
await ensureDisposableLib(rootDir);
await ensureOutputTarget(context);
await updatePackageJsonScripts(rootDir);
if (isNewProject) {
await generateExampleTests(rootDir);
}
prompts.log.info('Run "npx playwright install" to download the browser binaries before running tests.');
outro("Playwright configured");
}
},
generate: {
fileTemplates: [
{
label: "E2E test (.e2e.ts)",
extension: "e2e.ts",
selectedByDefault: true,
template: (tagName) => e2eSpecTemplate(tagName)
}
]
}
};
export {
wizard
};
//# sourceMappingURL=wizard.js.map