diff --git a/tests/load_workspace.spec.ts b/tests/load_workspace.spec.ts new file mode 100644 index 0000000..3d18793 --- /dev/null +++ b/tests/load_workspace.spec.ts @@ -0,0 +1,47 @@ +import { expect, test } from "@playwright/test"; +import { goToHomePage, mockShowOpenFilePicker } from "./utils"; + +test.beforeEach(goToHomePage); + +let test_files = [ + ["Old Xml format", "hello_world_xml.l_original_uno"], + ["New Json format", "hello_world_json.l_original_uno"], +]; + +for (let [testName, file] of test_files) { + test(`LoadAndSave - ${testName}`, async ({ page }) => { + await page.getByText("Leaphy Original").click(); + await page.getByText("Original Uno").click(); + + await page.getByRole("button", { name: "My projects" }).click(); + + // Playwright doesn't seem to support `showOpenFilePicker()` so mock it + let createOnWritePromise = await mockShowOpenFilePicker( + page, + `./tests/saved_workspaces/${file}`, + ); + + await page.getByRole("cell", { name: "Open" }).click(); + + // All of these should be loaded in + await expect(page.getByText("repeat")).toBeVisible(); + await expect(page.getByText("during")).toBeVisible(); + await expect(page.getByText("hello world!")).toBeVisible(); + await expect(page.getByText("10", { exact: true })).toBeVisible(); + + // The code should have also been updated + await page.locator(".side").first().click(); + + await expect(page.locator(".view-lines")).toContainText("test = 10"); + await expect(page.locator(".view-lines")).toContainText("delay(1000)"); + await expect(page.locator(".view-lines")).toContainText('"hello world!"'); + + // Save the project, it should write to the previously opened file! + await page.getByRole("button", { name: "My projects" }).click(); + + let onWritePromise = createOnWritePromise(); + await page.getByRole("cell", { name: "Save", exact: true }).click(); + let writtenChunks = await onWritePromise; + expect(JSON.stringify(writtenChunks)).toContain("hello world"); // This is very hacky, but it works + }); +} diff --git a/tests/saved_workspaces/hello_world_json.l_original_uno b/tests/saved_workspaces/hello_world_json.l_original_uno new file mode 100644 index 0000000..9cba444 --- /dev/null +++ b/tests/saved_workspaces/hello_world_json.l_original_uno @@ -0,0 +1 @@ +{"blocks":{"languageVersion":0,"blocks":[{"type":"leaphy_start","id":"rzE0Ve:6bHB~8aIqyj-U","x":886,"y":100,"deletable":false,"inputs":{"STACK":{"block":{"type":"variables_set","id":"J6[_UnII$nn5;n-D7P3$","fields":{"VAR":{"id":"9s2~[Z2`R|0j/j@6xBGP"}},"inputs":{"VALUE":{"block":{"type":"math_number","id":"pdF9j#htw{Xs`Cu^4CI2","fields":{"NUM":10}}}},"next":{"block":{"type":"controls_whileUntil","id":"I9R6ZGCPgw]-=bxAdMW2","fields":{"MODE":"WHILE"},"inputs":{"BOOL":{"block":{"type":"logic_compare","id":"M8H2-TdeZobyf|`u2QlU","fields":{"OP":"GT"},"inputs":{"A":{"shadow":{"type":"math_number","id":"/r$j5]H,4+TNE5I@zVBa","fields":{"NUM":1}},"block":{"type":"variables_get","id":"HZPG$vIAV]RO4P(`nv}W","fields":{"VAR":{"id":"9s2~[Z2`R|0j/j@6xBGP"}}}},"B":{"shadow":{"type":"math_number","id":"XWy#p7-oQ,Os9e6JHa2u","fields":{"NUM":1}}}}}},"DO":{"block":{"type":"variables_set","id":"@?8QYZJK+f[LGj)z;)?+","fields":{"VAR":{"id":"9s2~[Z2`R|0j/j@6xBGP"}},"inputs":{"VALUE":{"block":{"type":"math_arithmetic","id":"SYvyhH*I*Anmq+l5SUnX","fields":{"OP":"MINUS"},"inputs":{"A":{"shadow":{"type":"math_number","id":"4%ey;V{RbJ^B4*oTmf|B","fields":{"NUM":1}},"block":{"type":"variables_get","id":"3i@X_D@WuUJ@3YTZ#-r~","fields":{"VAR":{"id":"9s2~[Z2`R|0j/j@6xBGP"}}}},"B":{"shadow":{"type":"math_number","id":"Q{EOVYZiEAvuv}a1.n22","fields":{"NUM":1}}}}}}},"next":{"block":{"type":"leaphy_serial_print_line","id":"ls|Kp5hrouDK:I3Uw]qK","inputs":{"VALUE":{"shadow":{"type":"text","id":"q,gPeGJ7wv.=tMi_F{sg","fields":{"TEXT":"hello world!"}}}},"next":{"block":{"type":"time_delay","id":"OS,gB$GEcY(30Z6YSaL6","inputs":{"DELAY_TIME_MILI":{"shadow":{"type":"math_number","id":"u*S`VE2z(@NFcD8zV{ZN","fields":{"NUM":1000}}}}}}}}}}}}}}}}}]},"variables":[{"name":"test","id":"9s2~[Z2`R|0j/j@6xBGP"}]} \ No newline at end of file diff --git a/tests/saved_workspaces/hello_world_xml.l_original_uno b/tests/saved_workspaces/hello_world_xml.l_original_uno new file mode 100644 index 0000000..1278a8e --- /dev/null +++ b/tests/saved_workspaces/hello_world_xml.l_original_uno @@ -0,0 +1,81 @@ + + + test + + + + + test + + + 10 + + + + + WHILE + + + GT + + + 1 + + + test + + + + + 1 + + + + + + + test + + + MINUS + + + 1 + + + test + + + + + 1 + + + + + + + + + hello world! + + + + + + + 1000 + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/utils.ts b/tests/utils.ts index 6777e3e..eb68b76 100644 --- a/tests/utils.ts +++ b/tests/utils.ts @@ -1,3 +1,4 @@ +import { promises as fs } from "node:fs"; import type { Page, PlaywrightTestArgs } from "@playwright/test"; export async function goToHomePage({ page }: PlaywrightTestArgs) { @@ -11,3 +12,63 @@ export async function openExample(page: Page, example: string | RegExp) { await page.getByRole("cell", { name: "Examples" }).click(); await page.getByRole("button", { name: example }).click(); } + +// Playwright doesn't seem to support `showOpenFilePicker()` so this functions mocks it +export async function mockShowOpenFilePicker( + page: Page, + path: string, +): Promise Promise>> { + let content = await fs.readFile(path, "utf-8"); + + await page.evaluate( + ([content, path]) => { + const blob = new Blob([content], { type: "application/json" }); + const file = new File([blob], path, { type: "application/json" }); + + const createWritable = async () => { + let writtenChunks: FileSystemWriteChunkType[] = []; + return { + write: async (data: FileSystemWriteChunkType) => { + writtenChunks.push(data); + }, + close: async () => { + await (window as any).onWriteOpenedFile(writtenChunks); + }, + }; + }; + + const fileHandle = { + getFile: async () => file, + name: path, + createWritable, + }; + + (window as any).showOpenFilePicker = async () => { + return [fileHandle]; + }; + }, + [content, path], + ); + + let createOnWritePromise = async (timeout = 1000) => { + let onWriteResolve: + | ((writtenChunks: FileSystemWriteChunkType[]) => void) + | undefined = undefined; + + await page.exposeFunction( + "onWriteOpenedFile", + (writtenChunks: FileSystemWriteChunkType[]) => { + if (onWriteResolve) onWriteResolve(writtenChunks); + }, + ); + + return new Promise((resolve, reject) => { + onWriteResolve = resolve; + setTimeout(() => { + reject("File not written within time!"); + }, timeout); + }); + }; + + return createOnWritePromise; +}