Make CI run cross-platform (#295)

This commit is contained in:
Will Chen
2025-06-03 13:04:16 -07:00
committed by GitHub
parent 83eb721323
commit 7235eab227
16 changed files with 149 additions and 59 deletions

View File

@@ -1,4 +1,4 @@
import { test } from "./helpers/test_helper";
import { test, Timeout } from "./helpers/test_helper";
import { expect } from "@playwright/test";
test("write to index, approve, check preview", async ({ po }) => {
@@ -11,6 +11,8 @@ test("write to index, approve, check preview", async ({ po }) => {
await po.snapshotMessages();
// This can be pretty slow because it's waiting for the app to build.
await expect(po.getPreviewIframeElement()).toBeVisible({ timeout: 15_000 });
await expect(po.getPreviewIframeElement()).toBeVisible({
timeout: Timeout.LONG,
});
await po.snapshotPreview();
});

View File

@@ -1,4 +1,4 @@
import { test } from "./helpers/test_helper";
import { test, Timeout } from "./helpers/test_helper";
import { expect } from "@playwright/test";
test("auto-approve", async ({ po }) => {
@@ -7,6 +7,8 @@ test("auto-approve", async ({ po }) => {
await po.snapshotMessages();
// This can be pretty slow because it's waiting for the app to build.
await expect(po.getPreviewIframeElement()).toBeVisible({ timeout: 15_000 });
await expect(po.getPreviewIframeElement()).toBeVisible({
timeout: Timeout.LONG,
});
await po.snapshotPreview();
});

View File

@@ -1,7 +1,10 @@
import { test } from "./helpers/test_helper";
import { testSkipIfWindows } from "./helpers/test_helper";
// This is useful to make sure the messages are being sent correctly.
test("dump messages", async ({ po }) => {
//
// Why skip on Windows? The file ordering is not stable between runs
// but unclear why.
testSkipIfWindows("dump messages", async ({ po }) => {
await po.setUp();
await po.sendPrompt("[dump]");
await po.snapshotServerDump();

View File

@@ -1 +1,3 @@
[[beginning of AI_RULES.md]]
There's already AI rules...
[[end of AI_RULES.md]]

View File

@@ -3,9 +3,16 @@ import { findLatestBuild, parseElectronApp } from "electron-playwright-helpers";
import { ElectronApplication, _electron as electron } from "playwright";
import fs from "fs";
import path from "path";
import os from "os";
const showDebugLogs = process.env.DEBUG_LOGS === "true";
export const Timeout = {
// Why make this a constant? In some platforms, perhaps locally,
// we may want to shorten this.
LONG: 30_000,
};
class PageObject {
private userDataDir: string;
@@ -99,7 +106,7 @@ class PageObject {
async snapshotPreview() {
const iframe = this.getPreviewIframeElement();
await expect(iframe.contentFrame().locator("body")).toMatchAriaSnapshot({
timeout: 30_000,
timeout: Timeout.LONG,
});
}
@@ -469,19 +476,31 @@ export const test = base.extend<{
});
await use(electronApp);
await electronApp.close();
// Why are we doing a force kill on Windows?
//
// Otherwise, Playwright will just hang on the test cleanup
// because the electron app does NOT ever fully quit due to
// Windows' strict resource locking (e.g. file locking).
if (os.platform() === "win32") {
electronApp.process().kill();
} else {
await electronApp.close();
}
},
{ auto: true },
],
});
// Wrapper that skips tests on Windows platform
export const testSkipIfWindows = os.platform() === "win32" ? test.skip : test;
function prettifyDump(
dumpContent: string,
{ onlyLastMessage = false }: { onlyLastMessage?: boolean } = {},
) {
const parsedDump = JSON.parse(dumpContent) as Array<{
role: string;
content: string;
content: string | Array<{}>;
}>;
const messages = onlyLastMessage ? parsedDump.slice(-1) : parsedDump;
@@ -491,6 +510,8 @@ function prettifyDump(
const content = Array.isArray(message.content)
? JSON.stringify(message.content)
: message.content
// Normalize line endings to always use \n
.replace(/\r\n/g, "\n")
// We remove package.json because it's flaky.
// Depending on whether pnpm install is run, it will be modified,
// and the contents and timestamp (thus affecting order) will be affected.

View File

@@ -1,4 +1,4 @@
import { test } from "./helpers/test_helper";
import { test, Timeout } from "./helpers/test_helper";
import { expect } from "@playwright/test";
import fs from "fs";
import path from "path";
@@ -15,7 +15,7 @@ test("rebuild app", async ({ po }) => {
await po.clickRebuild();
await expect(po.locateLoadingAppPreview()).toBeVisible();
await expect(po.locateLoadingAppPreview()).not.toBeVisible({
timeout: 15_000,
timeout: Timeout.LONG,
});
// Check that the file is removed with the rebuild

View File

@@ -1,4 +1,4 @@
import { test } from "./helpers/test_helper";
import { test, Timeout } from "./helpers/test_helper";
import { expect } from "@playwright/test";
test("restart app", async ({ po }) => {
@@ -8,7 +8,7 @@ test("restart app", async ({ po }) => {
await po.clickRestart();
await expect(po.locateLoadingAppPreview()).toBeVisible();
await expect(po.locateLoadingAppPreview()).not.toBeVisible({
timeout: 15_000,
timeout: Timeout.LONG,
});
await po.snapshotPreview();

View File

@@ -325,7 +325,9 @@ This structured thinking ensures you:
4. Maintain a consistent approach to problem-solving
[[beginning of AI_RULES.md]]
There's already AI rules...
[[end of AI_RULES.md]]
# REMEMBER
@@ -377,7 +379,14 @@ You need to first add Supabase to your app and then we can add auth.
===
role: user
message: This is my codebase. <dyad-file path="index.html">
message: This is my codebase. <dyad-file path="AI_RULES.md">
[[beginning of AI_RULES.md]]
There's already AI rules...
[[end of AI_RULES.md]]
</dyad-file>
<dyad-file path="index.html">
<!doctype html>
<html lang="en">
<head>
@@ -394,31 +403,6 @@ message: This is my codebase. <dyad-file path="index.html">
</dyad-file>
<dyad-file path="AI_RULES.md">
There's already AI rules...
</dyad-file>
<dyad-file path="vite.config.ts">
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react-swc";
import path from "path";
export default defineConfig(() => ({
server: {
host: "::",
port: 8080,
},
plugins: [react()],
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
},
},
}));
</dyad-file>
<dyad-file path="src/App.tsx">
const App = () => <div>Minimal imported app</div>;
@@ -439,6 +423,26 @@ createRoot(document.getElementById("root")!).render(<App />);
</dyad-file>
<dyad-file path="vite.config.ts">
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react-swc";
import path from "path";
export default defineConfig(() => ({
server: {
host: "::",
port: 8080,
},
plugins: [react()],
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
},
},
}));
</dyad-file>
===

View File

@@ -1,4 +1,4 @@
import { test } from "./helpers/test_helper";
import { test, Timeout } from "./helpers/test_helper";
import { expect } from "@playwright/test";
test("undo", async ({ po }) => {
@@ -11,7 +11,7 @@ test("undo", async ({ po }) => {
iframe.contentFrame().getByText("Testing:write-index(2)!"),
).toBeVisible({
// This can be pretty slow because it's waiting for the app to build.
timeout: 15_000,
timeout: Timeout.LONG,
});
await po.clickUndo();
@@ -20,7 +20,7 @@ test("undo", async ({ po }) => {
iframe.contentFrame().getByText("Testing:write-index!"),
).toBeVisible({
// Also, could be slow.
timeout: 15_000,
timeout: Timeout.LONG,
});
await po.clickUndo();
@@ -29,6 +29,6 @@ test("undo", async ({ po }) => {
iframe.contentFrame().getByText("Welcome to Your Blank App"),
).toBeVisible({
// Also, could be slow.
timeout: 15_000,
timeout: Timeout.LONG,
});
});