Skip to content

Commit

Permalink
Desktop E2E tests: show creation and selection (#88)
Browse files Browse the repository at this point in the history
* Desktop E2E show creation and selection

* Add assertion that the show load succeeded
  • Loading branch information
markspolakovs authored Sep 5, 2023
1 parent 2ff2ff5 commit 69a7c8c
Show file tree
Hide file tree
Showing 10 changed files with 164 additions and 9 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ jobs:
working-directory: ./server/.next/standalone

- name: Run Playwright tests
run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- yarn playwright test
run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- yarn test:e2e
working-directory: ./desktop
env:
PLAYWRIGHT_HTML_REPORT: ${{ github.workspace }}/server/playwright-report
Expand Down
63 changes: 63 additions & 0 deletions desktop/e2e/media.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/* eslint-disable no-empty-pattern */
import {
test as base,
_electron as electron,
expect,
type ElectronApplication,
type Page,
} from "@playwright/test";
import { server } from "./serverAPI";

const test = base.extend<{
app: [ElectronApplication, Page];
}>({
app: async ({}, use) => {
const app = await electron.launch({ args: [".vite/build/main.js"] });
const win = await app.firstWindow();

await win.waitForLoadState("domcontentloaded");

await win.getByLabel("Server address").fill("http://localhost:3000");
await win.getByLabel("Server Password").fill("aaa");

await win.getByRole("button", { name: "Connect" }).click();

await expect(
win.getByRole("heading", { name: "Select a show" }),
).toBeVisible();

await use([app, win]);

await expect(
app.evaluate(({ ipcMain }) => ipcMain.emit("resetTestSettings")),
).not.toBe(false);

await win.close();
await app.close();
},
});

test.beforeEach(async ({ request }) => {
await request.post(
"http://localhost:3000/api/resetDBInTestsDoNotUseOrYouWillBeFired",
);
await server.shows.create.mutate({
name: "Test Show",
start: new Date("2026-01-01T19:00:00Z"),
continuityItems: {
create: {
name: "Test Continuity",
durationSeconds: 0,
order: 0,
},
},
});
});

test("can select newly created show", async ({ app: [_app, page] }) => {
const row = page.getByRole("listitem").filter({ hasText: "Test Show" });
expect(row).toBeVisible();
const btn = row.getByRole("button", { name: "Select" });
await btn.click();
await expect(page.getByRole("button", { name: "Test Show" })).toBeVisible();
});
18 changes: 18 additions & 0 deletions desktop/e2e/serverAPI.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { fetch } from "undici";
import type { AppRouter } from "bowser-server/app/api/_router";
import { createTRPCProxyClient, httpBatchLink } from "@trpc/client";
import SuperJSON from "superjson";

export const server = createTRPCProxyClient<AppRouter>({
links: [
httpBatchLink({
url: "http://localhost:3000/api/trpc",
headers: () => ({
Authorization: "Bearer aaa",
}),
// @ts-expect-error the undici types don't match what TRPC is expecting, but they're close enough
fetch,
}),
],
transformer: SuperJSON,
});
4 changes: 3 additions & 1 deletion desktop/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
"lint": "eslint src/",
"prettify": "prettier --write .",
"typecheck": "tsc --noEmit",
"test": "vitest run"
"test": "vitest run",
"test:e2e": "E2E_TEST=true playwright test"
},
"keywords": [],
"author": {
Expand Down Expand Up @@ -48,6 +49,7 @@
"prettier": "^3.0.3",
"prettier-plugin-tailwindcss": "^0.4.1",
"tailwindcss": "^3.3.3",
"undici": "^5.23.0",
"vite": "^4.4.6",
"vitest": "^0.33.0"
},
Expand Down
2 changes: 1 addition & 1 deletion desktop/src/main/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ const createWindow = async () => {
}

// Open the DevTools.
if (import.meta.env.DEV) {
if (import.meta.env.DEV || process.env.BOWSER_OPEN_DEVTOOLS === "true") {
mainWindow.webContents.openDevTools();
}

Expand Down
30 changes: 29 additions & 1 deletion desktop/src/main/settings.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,37 @@
import settings from "electron-settings";
import electronSettings from "electron-settings";
import { safeStorage } from "./safeStorage";
import { z } from "zod";
import * as fsp from "fs/promises";
import { AssetTypeSchema } from "@bowser/prisma/types";
import { IPCEvents } from "./ipcEventBus";
import { ipcMain } from "electron";

/*
* In E2E tests we don't want settings to persist between tests, so we use
* an in-memory store that can be reset by the test harness.
*/

interface SettingsStore {
get(key: string): Promise<unknown | undefined>;
set(key: string, value: unknown): Promise<void>;
}

const inMemSettings = new Map<string, unknown>();
const inMemSettingsStore: SettingsStore = {
async get(key: string) {
return inMemSettings.get(key);
},
async set(key: string, value: unknown) {
inMemSettings.set(key, value);
},
};

const settings: SettingsStore =
process.env.E2E_TEST === "true" ? inMemSettingsStore : electronSettings;

ipcMain.on("resetTestSettings", () => {
inMemSettings.clear();
});

/**
* Since settings are stored as JSON files on disk, we pass them through zod as a sanity check.
Expand Down
4 changes: 2 additions & 2 deletions desktop/src/renderer/ConnectAndSelectShowGate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,9 @@ export function SelectShowForm(props: { onSelect?: () => void }) {
}
invariant(listShows.data, "listShows.data is null");
return (
<div>
<div data-testid="SelectShowForm.showsList" role="list">
{listShows.data.map((show) => (
<div key={show.id}>
<div key={show.id} role="listitem">
<h3 className="text-xl">{show.name}</h3>
<Button
color="primary"
Expand Down
11 changes: 10 additions & 1 deletion server/app/api/_base.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
import { initTRPC } from "@trpc/server";
import { TRPCError, initTRPC } from "@trpc/server";
import superjson from "superjson";

const t = initTRPC.create({
transformer: superjson,
});

export const publicProcedure = t.procedure;
export const e2eProcedure = t.procedure.use(({ next }) => {
if (process.env.E2E_TEST !== "true") {
throw new TRPCError({
code: "FORBIDDEN",
message: "This endpoint is only available in e2e tests",
});
}
return next();
});
export const router = t.router;
38 changes: 36 additions & 2 deletions server/app/api/_router.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { publicProcedure, router } from "./_base";
import { e2eProcedure, publicProcedure, router } from "./_base";
import { z } from "zod";
import { db } from "@/lib/db";
import {
Expand All @@ -8,7 +8,11 @@ import {
PartialShowModel,
} from "@bowser/prisma/utilityTypes";
import { getPresignedURL } from "@/lib/s3";
import { ContinuityItemSchema, RundownItemSchema } from "@bowser/prisma/types";
import {
ContinuityItemSchema,
RundownItemSchema,
ShowCreateInputSchema,
} from "@bowser/prisma/types";

const ExtendedMediaModelWithDownloadURL = CompleteMediaModel.extend({
continuityItem: ContinuityItemSchema.nullable(),
Expand Down Expand Up @@ -80,6 +84,36 @@ export const appRouter = router({
});
return obj;
}),
create: e2eProcedure
.input(ShowCreateInputSchema)
.output(CompleteShowModel)
.mutation(async ({ input }) => {
const res = await db.show.create({
data: input,
include: {
continuityItems: {
include: {
media: true,
},
},
rundowns: {
include: {
items: {
include: {
media: true,
},
},
assets: {
include: {
media: true,
},
},
},
},
},
});
return res;
}),
}),
media: router({
get: publicProcedure
Expand Down
1 change: 1 addition & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5805,6 +5805,7 @@ __metadata:
superjson: ^1.13.1
tailwindcss: ^3.3.3
typescript: ^5.1.6
undici: ^5.23.0
utf-8-validate: ^6.0.3
uuid: ^9.0.0
vite: ^4.4.6
Expand Down

0 comments on commit 69a7c8c

Please sign in to comment.