use super::Edits;
use crate::{
CliResult,
names::{humanize, is_identifier},
output::CliError,
project::Project,
};
pub const SYSTEM_TESTS: &str = "tests/system";
pub const PLAYWRIGHT_VERSION: &str = "1.63.0";
const CONFIG: &str = r#"// System tests (`ocre g system_test`): a real browser against the test server
// that `ocre test --e2e` starts, which sets BASE_URL. `npx playwright test --ui`
// runs them interactively against `ocre dev` (BASE_URL=http://localhost:8787).
import { defineConfig, devices } from "@playwright/test";
export default defineConfig({
testDir: "tests/system",
// The test server shares one database: one test at a time.
workers: 1,
use: {
baseURL: process.env.BASE_URL ?? "http://localhost:8788",
// Failed tests keep a screenshot and a trace in test-results/.
screenshot: "only-on-failure",
trace: "retain-on-failure",
},
// Each test runs once per screen. Other browsers: devices["Desktop Firefox"],
// devices["Desktop Safari"] (then `npx playwright install firefox webkit`).
projects: [
{ name: "desktop", use: { ...devices["Desktop Chrome"] } },
{ name: "phone", use: { ...devices["Pixel 7"] } },
],
});
"#;
pub fn system_test(project: &Project, name: &str) -> CliResult {
if !is_identifier(name) {
return Err(CliError::new(format!("invalid system test name `{name}`"))
.hint("use snake_case starting with a letter, e.g. `ocre g system_test signing_up`"));
}
let mut edits = Edits::new(project);
if !edits.exists("playwright.config.ts") {
edits.create("playwright.config.ts", CONFIG.to_owned())?;
let package = edits.read("package.json")?.unwrap_or_default();
let marker = "\"devDependencies\": {\n";
let at = package.find(marker).ok_or_else(|| {
CliError::new("package.json has no `\"devDependencies\": {` block")
.hint("restore the package.json `ocre new` writes, or add `\"@playwright/test\"` to it by hand")
})?;
let line = format!(" \"@playwright/test\": \"{PLAYWRIGHT_VERSION}\",\n");
let at = at + marker.len();
edits.update("package.json", format!("{}{line}{}", &package[..at], &package[at..]));
let ignore = edits.read(".gitignore")?.unwrap_or_default();
if !ignore.contains("test-results/") {
edits.update(
".gitignore",
format!("{ignore}# Playwright (ocre g system_test)\ntest-results/\nplaywright-report/\n"),
);
}
}
let title = humanize(name);
edits.create(
&format!("{SYSTEM_TESTS}/{name}.spec.ts"),
format!(
r#"// {title}: a browser test. Generated by `ocre g system_test {name}`; runs with `ocre test --e2e`.
// Assertions wait for the page (https://playwright.dev/docs/test-assertions).
import {{ expect, test }} from "@playwright/test";
test("{title}", async ({{ page }}) => {{
await page.goto("/");
await expect(page.locator("body")).toBeVisible();
// await page.getByRole("link", {{ name: "New post" }}).click();
// await page.getByLabel("Title").fill("Hello");
// await page.getByRole("button", {{ name: "Save" }}).click();
// await expect(page.getByText("Post was successfully created.")).toBeVisible();
}});
"#
),
)?;
let mut report = edits.apply("generate system_test")?;
report.next = vec![
"npm install".to_owned(),
"npx playwright install chromium (once per machine)".to_owned(),
format!("edit {SYSTEM_TESTS}/{name}.spec.ts, then ocre test --e2e"),
];
Ok(report)
}