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;
+}