From 6f10f15048cfc8aaba7736305bb3011b76d3cf08 Mon Sep 17 00:00:00 2001 From: Alexander Thor Date: Tue, 11 Feb 2025 16:27:14 +0100 Subject: [PATCH 01/37] feat: first rpc stub --- web-admin/src/routes/-/embed/+layout.svelte | 6 +++ .../DashboardURLStateSyncWrapper.svelte | 1 + web-common/src/lib/rpc.ts | 53 +++++++++++++++++++ 3 files changed, 60 insertions(+) create mode 100644 web-common/src/lib/rpc.ts diff --git a/web-admin/src/routes/-/embed/+layout.svelte b/web-admin/src/routes/-/embed/+layout.svelte index a242293366b..d603455d0bf 100644 --- a/web-admin/src/routes/-/embed/+layout.svelte +++ b/web-admin/src/routes/-/embed/+layout.svelte @@ -1,12 +1,18 @@ Promise | unknown; +}; + +const methods: RPCMethods = { + echo(message: { message: string }) { + return message; + }, + setTheme({ theme }: { theme: string }) { + document.documentElement.setAttribute("data-theme", theme); + return { success: true }; + } +}; + +async function handleRPCMessage(event: MessageEvent) { + + const { id, method, params } = event.data; + + if (methods[method]) { + try { + const result = await methods[method](params); + if (id) { + event.source?.postMessage({ id, result } as RPCResponse); + } + } catch (error) { + if (id) { + event.source?.postMessage({ id, error: (error as Error).message } as RPCResponse); + } + } + } +} + +export function initRPC() { + window.addEventListener("message", (event) => { + void handleRPCMessage(event); + }); +} + +export function registerMethod(name: string, func: (params: T) => Promise | unknown) { + methods[name] = func; +} From 81d65838eaf58dcc9c1b087a640bf798b81f1012 Mon Sep 17 00:00:00 2001 From: Alexander Thor Date: Tue, 11 Feb 2025 19:39:13 +0100 Subject: [PATCH 02/37] add emit --- web-common/src/lib/rpc.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/web-common/src/lib/rpc.ts b/web-common/src/lib/rpc.ts index d847e6f945a..8a8c71e8b9b 100644 --- a/web-common/src/lib/rpc.ts +++ b/web-common/src/lib/rpc.ts @@ -51,3 +51,9 @@ export function initRPC() { export function registerMethod(name: string, func: (params: T) => Promise | unknown) { methods[name] = func; } + +export function emit(method: string, params?: unknown) { + if (window.parent !== window) { + window.parent.postMessage({ method, params } as RPCRequest, "*"); + } +} From 63f5d76b7c7338c12335d216c3448d0a0ee4509d Mon Sep 17 00:00:00 2001 From: Alexander Thor Date: Fri, 14 Feb 2025 10:34:48 +0100 Subject: [PATCH 03/37] feat: embed public api --- .../src/features/embeds/EmbedPublicAPI.svelte | 85 +++++++++++++++++++ .../src/features/embeds/ExploreEmbed.svelte | 7 +- web-common/src/lib/rpc.ts | 13 ++- 3 files changed, 97 insertions(+), 8 deletions(-) create mode 100644 web-admin/src/features/embeds/EmbedPublicAPI.svelte diff --git a/web-admin/src/features/embeds/EmbedPublicAPI.svelte b/web-admin/src/features/embeds/EmbedPublicAPI.svelte new file mode 100644 index 00000000000..ea17906d14a --- /dev/null +++ b/web-admin/src/features/embeds/EmbedPublicAPI.svelte @@ -0,0 +1,85 @@ + + + diff --git a/web-admin/src/features/embeds/ExploreEmbed.svelte b/web-admin/src/features/embeds/ExploreEmbed.svelte index 3e5db6dcde5..f5aac8f30a5 100644 --- a/web-admin/src/features/embeds/ExploreEmbed.svelte +++ b/web-admin/src/features/embeds/ExploreEmbed.svelte @@ -3,6 +3,8 @@ import DashboardThemeProvider from "@rilldata/web-common/features/dashboards/DashboardThemeProvider.svelte"; import DashboardURLStateSyncWrapper from "@rilldata/web-common/features/dashboards/url-state/DashboardURLStateSyncWrapper.svelte"; import StateManagersProvider from "@rilldata/web-common/features/dashboards/state-managers/StateManagersProvider.svelte"; + import EmbedPublicAPI from "@rilldata/web-admin/features/embeds/EmbedPublicAPI.svelte"; + import { createRuntimeServiceGetExplore } from "@rilldata/web-common/runtime-client"; import { errorStore } from "../errors/error-store"; @@ -14,6 +16,7 @@ }); $: ({ isSuccess, isError, error, data } = $explore); $: isExploreNotFound = isError && error?.response?.status === 404; + // We check for explore.state.validSpec instead of meta.reconcileError. validSpec persists // from previous valid explores, allowing display even when the current explore spec is invalid // and a meta.reconcileError exists. @@ -39,7 +42,9 @@ - + + + diff --git a/web-common/src/lib/rpc.ts b/web-common/src/lib/rpc.ts index 8a8c71e8b9b..9d5cfdfb990 100644 --- a/web-common/src/lib/rpc.ts +++ b/web-common/src/lib/rpc.ts @@ -17,10 +17,6 @@ type RPCMethods = { const methods: RPCMethods = { echo(message: { message: string }) { return message; - }, - setTheme({ theme }: { theme: string }) { - document.documentElement.setAttribute("data-theme", theme); - return { success: true }; } }; @@ -43,8 +39,11 @@ async function handleRPCMessage(event: MessageEvent) { } export function initRPC() { - window.addEventListener("message", (event) => { - void handleRPCMessage(event); + window.removeEventListener("message", (_event: MessageEvent) => { }) + window.addEventListener("message", (event: MessageEvent) => { + if (event.source && event.data) { + void handleRPCMessage(event as MessageEvent); + } }); } @@ -54,6 +53,6 @@ export function registerMethod(name: string, func: (params: T) => Promise Date: Fri, 14 Feb 2025 10:39:28 +0100 Subject: [PATCH 04/37] fix: better name for state stream --- web-admin/src/features/embeds/EmbedPublicAPI.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web-admin/src/features/embeds/EmbedPublicAPI.svelte b/web-admin/src/features/embeds/EmbedPublicAPI.svelte index ea17906d14a..c032866fc79 100644 --- a/web-admin/src/features/embeds/EmbedPublicAPI.svelte +++ b/web-admin/src/features/embeds/EmbedPublicAPI.svelte @@ -77,7 +77,7 @@ ); // Stream the state to the parent - $: emit("state", { + $: emit("stateChange", { state: stateString, }); From ba6199545f17bafc4c8c683b4c1eabd091464eb9 Mon Sep 17 00:00:00 2001 From: Alexander Thor Date: Fri, 14 Feb 2025 10:56:13 +0100 Subject: [PATCH 05/37] fix: error handling and better spec conformatiy --- web-common/src/lib/rpc.ts | 80 ++++++++++++++++++++++++++++----------- 1 file changed, 58 insertions(+), 22 deletions(-) diff --git a/web-common/src/lib/rpc.ts b/web-common/src/lib/rpc.ts index 9d5cfdfb990..853064c3a2c 100644 --- a/web-common/src/lib/rpc.ts +++ b/web-common/src/lib/rpc.ts @@ -1,48 +1,84 @@ -type RPCRequest = { - id: string; +type JSONRPCRequest = { + jsonrpc: "2.0"; + id: string | number | null; method: string; params?: unknown; }; -type RPCResponse = { - id: string; +type JSONRPCResponse = { + jsonrpc: "2.0"; + id: string | number | null; result?: unknown; - error?: string; + error?: { + code: number; + message: string; + data?: unknown; + }; }; -type RPCMethods = { +const JSONRPC_ERRORS = { + PARSE_ERROR: { code: -32700, message: "Parse error" }, + INVALID_REQUEST: { code: -32600, message: "Invalid Request" }, + METHOD_NOT_FOUND: { code: -32601, message: "Method not found" }, + INVALID_PARAMS: { code: -32602, message: "Invalid params" }, + INTERNAL_ERROR: { code: -32603, message: "Internal error" }, +}; + +type JSONRPCMethods = { [key: string]: (params?: unknown) => Promise | unknown; }; -const methods: RPCMethods = { +const methods: JSONRPCMethods = { echo(message: { message: string }) { return message; } }; -async function handleRPCMessage(event: MessageEvent) { +async function handleRPCMessage(event: MessageEvent) { + if (typeof event.data !== "object" || event.data === null) { + return sendError(null, JSONRPC_ERRORS.INVALID_REQUEST); + } const { id, method, params } = event.data; - if (methods[method]) { - try { - const result = await methods[method](params); - if (id) { - event.source?.postMessage({ id, result } as RPCResponse); - } - } catch (error) { - if (id) { - event.source?.postMessage({ id, error: (error as Error).message } as RPCResponse); - } + if (typeof method !== "string" || (id !== null && typeof id !== "string" && typeof id !== "number")) { + return sendError(id, JSONRPC_ERRORS.INVALID_REQUEST); + } + + if (!methods[method]) { + return sendError(id, JSONRPC_ERRORS.METHOD_NOT_FOUND); + } + + try { + const result = await methods[method](params); + if (id !== null) { + sendResponse(id, result); } + } catch (error) { + sendError(id, { + code: JSONRPC_ERRORS.INTERNAL_ERROR.code, + message: (error as Error).message, + }); + } +} + +function sendResponse(id: string | number | null, result: unknown) { + if (window.parent !== window) { + window.parent.postMessage({ jsonrpc: "2.0", id, result } as JSONRPCResponse, "*"); + } +} + +function sendError(id: string | number | null, error: { code: number; message: string; data?: unknown }) { + if (window.parent !== window) { + window.parent.postMessage({ jsonrpc: "2.0", id, error } as JSONRPCResponse, "*"); } } export function initRPC() { - window.removeEventListener("message", (_event: MessageEvent) => { }) + window.removeEventListener("message", (_event: MessageEvent) => { }); window.addEventListener("message", (event: MessageEvent) => { if (event.source && event.data) { - void handleRPCMessage(event as MessageEvent); + void handleRPCMessage(event as MessageEvent); } }); } @@ -51,8 +87,8 @@ export function registerMethod(name: string, func: (params: T) => Promise Date: Fri, 14 Feb 2025 11:15:16 +0100 Subject: [PATCH 06/37] fix: cleanup --- .../dashboards/url-state/DashboardURLStateSyncWrapper.svelte | 1 - 1 file changed, 1 deletion(-) diff --git a/web-common/src/features/dashboards/url-state/DashboardURLStateSyncWrapper.svelte b/web-common/src/features/dashboards/url-state/DashboardURLStateSyncWrapper.svelte index 5fb9fdd74a7..6b7d375a233 100644 --- a/web-common/src/features/dashboards/url-state/DashboardURLStateSyncWrapper.svelte +++ b/web-common/src/features/dashboards/url-state/DashboardURLStateSyncWrapper.svelte @@ -9,7 +9,6 @@ import { getExploreStates } from "@rilldata/web-common/features/explores/selectors"; import type { V1ExplorePreset } from "@rilldata/web-common/runtime-client"; import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; - import { registerMethod } from "@rilldata/web-common/lib/rpc"; /** * Temporary wrapper component that mimics the parsing and loading of url into metrics. From 4065767757e7356e0fd146eda845dc3331eb2a29 Mon Sep 17 00:00:00 2001 From: Alexander Thor Date: Fri, 14 Feb 2025 11:44:21 +0100 Subject: [PATCH 07/37] fix: format --- web-common/src/lib/rpc.ts | 142 ++++++++++++++++++++++---------------- 1 file changed, 82 insertions(+), 60 deletions(-) diff --git a/web-common/src/lib/rpc.ts b/web-common/src/lib/rpc.ts index 853064c3a2c..75d93c8a871 100644 --- a/web-common/src/lib/rpc.ts +++ b/web-common/src/lib/rpc.ts @@ -1,94 +1,116 @@ type JSONRPCRequest = { - jsonrpc: "2.0"; - id: string | number | null; - method: string; - params?: unknown; + jsonrpc: "2.0"; + id: string | number | null; + method: string; + params?: unknown; }; type JSONRPCResponse = { - jsonrpc: "2.0"; - id: string | number | null; - result?: unknown; - error?: { - code: number; - message: string; - data?: unknown; - }; + jsonrpc: "2.0"; + id: string | number | null; + result?: unknown; + error?: { + code: number; + message: string; + data?: unknown; + }; }; const JSONRPC_ERRORS = { - PARSE_ERROR: { code: -32700, message: "Parse error" }, - INVALID_REQUEST: { code: -32600, message: "Invalid Request" }, - METHOD_NOT_FOUND: { code: -32601, message: "Method not found" }, - INVALID_PARAMS: { code: -32602, message: "Invalid params" }, - INTERNAL_ERROR: { code: -32603, message: "Internal error" }, + PARSE_ERROR: { code: -32700, message: "Parse error" }, + INVALID_REQUEST: { code: -32600, message: "Invalid Request" }, + METHOD_NOT_FOUND: { code: -32601, message: "Method not found" }, + INVALID_PARAMS: { code: -32602, message: "Invalid params" }, + INTERNAL_ERROR: { code: -32603, message: "Internal error" }, }; type JSONRPCMethods = { - [key: string]: (params?: unknown) => Promise | unknown; + [key: string]: (params?: unknown) => Promise | unknown; }; const methods: JSONRPCMethods = { - echo(message: { message: string }) { - return message; - } + echo(message: { message: string }) { + return message; + }, }; async function handleRPCMessage(event: MessageEvent) { - if (typeof event.data !== "object" || event.data === null) { - return sendError(null, JSONRPC_ERRORS.INVALID_REQUEST); - } + if (typeof event.data !== "object" || event.data === null) { + return sendError(null, JSONRPC_ERRORS.INVALID_REQUEST); + } - const { id, method, params } = event.data; + const { id, method, params } = event.data; - if (typeof method !== "string" || (id !== null && typeof id !== "string" && typeof id !== "number")) { - return sendError(id, JSONRPC_ERRORS.INVALID_REQUEST); - } + if ( + typeof method !== "string" || + (id !== null && typeof id !== "string" && typeof id !== "number") + ) { + return sendError(id, JSONRPC_ERRORS.INVALID_REQUEST); + } - if (!methods[method]) { - return sendError(id, JSONRPC_ERRORS.METHOD_NOT_FOUND); - } + if (!methods[method]) { + return sendError(id, JSONRPC_ERRORS.METHOD_NOT_FOUND); + } - try { - const result = await methods[method](params); - if (id !== null) { - sendResponse(id, result); - } - } catch (error) { - sendError(id, { - code: JSONRPC_ERRORS.INTERNAL_ERROR.code, - message: (error as Error).message, - }); + try { + const result = await methods[method](params); + if (id !== null) { + sendResponse(id, result); } + } catch (error) { + sendError(id, { + code: JSONRPC_ERRORS.INTERNAL_ERROR.code, + message: (error as Error).message, + }); + } } function sendResponse(id: string | number | null, result: unknown) { - if (window.parent !== window) { - window.parent.postMessage({ jsonrpc: "2.0", id, result } as JSONRPCResponse, "*"); - } + if (window.parent !== window) { + window.parent.postMessage( + { jsonrpc: "2.0", id, result } as JSONRPCResponse, + "*", + ); + } } -function sendError(id: string | number | null, error: { code: number; message: string; data?: unknown }) { - if (window.parent !== window) { - window.parent.postMessage({ jsonrpc: "2.0", id, error } as JSONRPCResponse, "*"); - } +function sendError( + id: string | number | null, + error: { code: number; message: string; data?: unknown }, +) { + if (window.parent !== window) { + window.parent.postMessage( + { jsonrpc: "2.0", id, error } as JSONRPCResponse, + "*", + ); + } } export function initRPC() { - window.removeEventListener("message", (_event: MessageEvent) => { }); - window.addEventListener("message", (event: MessageEvent) => { - if (event.source && event.data) { - void handleRPCMessage(event as MessageEvent); - } - }); + window.removeEventListener("message", (_event: MessageEvent) => {}); + window.addEventListener("message", (event: MessageEvent) => { + if (event.source && event.data) { + void handleRPCMessage(event as MessageEvent); + } + }); } -export function registerMethod(name: string, func: (params: T) => Promise | unknown) { - methods[name] = func; +export function registerMethod( + name: string, + func: (params: T) => Promise | unknown, +) { + methods[name] = func; } -export function emit(method: string, params?: unknown, id: string | number | null = null) { - if (window.parent !== window) { - window.parent.postMessage({ jsonrpc: "2.0", id, method, params } as JSONRPCRequest, "*"); - } +export function emit( + method: string, + params?: unknown, + id: string | number | null = null, +) { + if (window.parent !== window) { + window.parent.postMessage( + { jsonrpc: "2.0", id, method, params } as JSONRPCRequest, + "*", + ); + } } From 00a065b32818703e88062bdb3cadd336c5e62ac5 Mon Sep 17 00:00:00 2001 From: Alexander Thor Date: Sun, 16 Feb 2025 15:02:06 +0100 Subject: [PATCH 08/37] fix: make id optional --- web-common/src/lib/rpc.ts | 138 +++++++++++++++++--------------------- 1 file changed, 60 insertions(+), 78 deletions(-) diff --git a/web-common/src/lib/rpc.ts b/web-common/src/lib/rpc.ts index 75d93c8a871..43c67961bee 100644 --- a/web-common/src/lib/rpc.ts +++ b/web-common/src/lib/rpc.ts @@ -1,116 +1,98 @@ type JSONRPCRequest = { - jsonrpc: "2.0"; - id: string | number | null; - method: string; - params?: unknown; + id?: string | number | null; + method: string; + params?: unknown; }; type JSONRPCResponse = { - jsonrpc: "2.0"; - id: string | number | null; - result?: unknown; - error?: { - code: number; - message: string; - data?: unknown; - }; + id?: string | number | null; + result?: unknown; + error?: { + code: number; + message: string; + data?: unknown; + }; }; const JSONRPC_ERRORS = { - PARSE_ERROR: { code: -32700, message: "Parse error" }, - INVALID_REQUEST: { code: -32600, message: "Invalid Request" }, - METHOD_NOT_FOUND: { code: -32601, message: "Method not found" }, - INVALID_PARAMS: { code: -32602, message: "Invalid params" }, - INTERNAL_ERROR: { code: -32603, message: "Internal error" }, + PARSE_ERROR: { code: -32700, message: "Parse error" }, + INVALID_REQUEST: { code: -32600, message: "Invalid Request" }, + METHOD_NOT_FOUND: { code: -32601, message: "Method not found" }, + INVALID_PARAMS: { code: -32602, message: "Invalid params" }, + INTERNAL_ERROR: { code: -32603, message: "Internal error" }, }; type JSONRPCMethods = { - [key: string]: (params?: unknown) => Promise | unknown; + [key: string]: (params?: unknown) => Promise | unknown; }; const methods: JSONRPCMethods = { - echo(message: { message: string }) { - return message; - }, + echo(message: { message: string }) { + return message; + }, }; async function handleRPCMessage(event: MessageEvent) { - if (typeof event.data !== "object" || event.data === null) { - return sendError(null, JSONRPC_ERRORS.INVALID_REQUEST); - } + if (typeof event.data !== "object" || event.data === null) { + return sendError(null, JSONRPC_ERRORS.INVALID_REQUEST); + } - const { id, method, params } = event.data; + const { id = null, method, params } = event.data; - if ( - typeof method !== "string" || - (id !== null && typeof id !== "string" && typeof id !== "number") - ) { - return sendError(id, JSONRPC_ERRORS.INVALID_REQUEST); - } + if (typeof method !== "string") { + return sendError(id, JSONRPC_ERRORS.INVALID_REQUEST); + } - if (!methods[method]) { - return sendError(id, JSONRPC_ERRORS.METHOD_NOT_FOUND); - } + if (!methods[method]) { + return sendError(id, JSONRPC_ERRORS.METHOD_NOT_FOUND); + } - try { - const result = await methods[method](params); - if (id !== null) { - sendResponse(id, result); + try { + const result = await methods[method](params); + if (id !== null) { + sendResponse(id, result); + } + } catch (error) { + sendError(id, { + code: JSONRPC_ERRORS.INTERNAL_ERROR.code, + message: (error as Error).message, + }); } - } catch (error) { - sendError(id, { - code: JSONRPC_ERRORS.INTERNAL_ERROR.code, - message: (error as Error).message, - }); - } } function sendResponse(id: string | number | null, result: unknown) { - if (window.parent !== window) { - window.parent.postMessage( - { jsonrpc: "2.0", id, result } as JSONRPCResponse, - "*", - ); - } + if (window.parent !== window) { + window.parent.postMessage({ id, result } as JSONRPCResponse, "*"); + } } function sendError( - id: string | number | null, - error: { code: number; message: string; data?: unknown }, + id: string | number | null, + error: { code: number; message: string; data?: unknown }, ) { - if (window.parent !== window) { - window.parent.postMessage( - { jsonrpc: "2.0", id, error } as JSONRPCResponse, - "*", - ); - } + if (window.parent !== window) { + window.parent.postMessage({ id, error } as JSONRPCResponse, "*"); + } } export function initRPC() { - window.removeEventListener("message", (_event: MessageEvent) => {}); - window.addEventListener("message", (event: MessageEvent) => { - if (event.source && event.data) { - void handleRPCMessage(event as MessageEvent); - } - }); + window.removeEventListener("message", (_event: MessageEvent) => { }); + window.addEventListener("message", (event: MessageEvent) => { + if (event.source && event.data) { + void handleRPCMessage(event as MessageEvent); + } + }); } export function registerMethod( - name: string, - func: (params: T) => Promise | unknown, + name: string, + func: (params: T) => Promise | unknown, ) { - methods[name] = func; + methods[name] = func; } -export function emit( - method: string, - params?: unknown, - id: string | number | null = null, -) { - if (window.parent !== window) { - window.parent.postMessage( - { jsonrpc: "2.0", id, method, params } as JSONRPCRequest, - "*", - ); - } +export function emit(method: string, params?: unknown) { + if (window.parent !== window) { + window.parent.postMessage({ method, params } as JSONRPCRequest, "*"); + } } From 2e3308232c5d628da04b862303b04e11337f568f Mon Sep 17 00:00:00 2001 From: Alexander Thor Date: Sun, 16 Feb 2025 19:56:37 +0100 Subject: [PATCH 09/37] fix: first stub of tests --- web-admin/playwright.config.ts | 18 +++++++++--------- web-admin/tests/setup/constants.ts | 2 ++ web-admin/tests/setup/github-session-setup.ts | 4 ++++ web-admin/tests/setup/setup.ts | 14 ++++++++++++-- 4 files changed, 27 insertions(+), 11 deletions(-) diff --git a/web-admin/playwright.config.ts b/web-admin/playwright.config.ts index 74da3980ff8..4baafbb5610 100644 --- a/web-admin/playwright.config.ts +++ b/web-admin/playwright.config.ts @@ -18,7 +18,7 @@ const config: PlaywrightTestConfig = { }, testDir: "tests", projects: [ - ...(process.env.CI + /* ...(process.env.CI ? [] // skip in CI : [ { @@ -29,7 +29,7 @@ const config: PlaywrightTestConfig = { name: "setup-github-session", testMatch: "github-session-setup.ts", }, - ]), + ]),*/ { name: "setup", testMatch: "setup.ts", @@ -40,14 +40,14 @@ const config: PlaywrightTestConfig = { ...(process.env.CI ? [] // skip in CI, since the GitHub Action uses an ephemeral runner : [ - { - name: "teardown", - testMatch: "teardown.ts", - use: { - storageState: ADMIN_STORAGE_STATE, - }, + { + name: "teardown", + testMatch: "teardown.ts", + use: { + storageState: ADMIN_STORAGE_STATE, }, - ]), + }, + ]), { name: "e2e", dependencies: process.env.E2E_NO_SETUP_OR_TEARDOWN ? [] : ["setup"], diff --git a/web-admin/tests/setup/constants.ts b/web-admin/tests/setup/constants.ts index 34c0b03e26d..471c65fed76 100644 --- a/web-admin/tests/setup/constants.ts +++ b/web-admin/tests/setup/constants.ts @@ -4,3 +4,5 @@ export const VIEWER_STORAGE_STATE = "playwright/.auth/viewer.json"; export const RILL_DEVTOOL_BACKGROUND_PROCESS_PID_FILE = "playwright/rill-devtool-background-process-pid.txt"; + +export const RILL_EMBED_TOKEN = "playwright/rill-embed-token.txt"; \ No newline at end of file diff --git a/web-admin/tests/setup/github-session-setup.ts b/web-admin/tests/setup/github-session-setup.ts index a05d8723fc0..34f7a1e78dc 100644 --- a/web-admin/tests/setup/github-session-setup.ts +++ b/web-admin/tests/setup/github-session-setup.ts @@ -5,6 +5,7 @@ import { updateEnvVariable } from "../utils/dotenv"; import { execAsync } from "../utils/spawn"; import { test as setup } from "./base"; import { GITHUB_STORAGE_STATE } from "./constants"; +import dotenv from "dotenv"; const githubEnvVarName = "RILL_DEVTOOL_E2E_GITHUB_STORAGE_STATE_JSON"; @@ -14,6 +15,9 @@ const envFilePath = path.resolve(__dirname, "../../../.env"); setup( "should authenticate to GitHub and save the session", async ({ page }) => { + + // Load environment variables from our root `.env` file + dotenv.config({ path: envFilePath }); if ( !process.env.RILL_DEVTOOL_E2E_ADMIN_ACCOUNT_EMAIL || !process.env.RILL_DEVTOOL_E2E_ADMIN_ACCOUNT_PASSWORD diff --git a/web-admin/tests/setup/setup.ts b/web-admin/tests/setup/setup.ts index ad243d22c1b..8ee3c0799c0 100644 --- a/web-admin/tests/setup/setup.ts +++ b/web-admin/tests/setup/setup.ts @@ -11,6 +11,7 @@ import { test as setup } from "./base"; import { ADMIN_STORAGE_STATE, RILL_DEVTOOL_BACKGROUND_PROCESS_PID_FILE, + RILL_EMBED_TOKEN, } from "./constants"; import { cliLogin } from "./fixtures/cli"; @@ -27,7 +28,7 @@ setup( // Start the cloud dependencies via Docker // This will block until the services are ready await spawnAndMatch( - "rill", + "./rill", ["devtool", "start", "e2e", "--reset", "--only", "deps"], /All services ready/, { @@ -128,6 +129,13 @@ setup( const { stdout: orgCreateStdout } = await execAsync("rill org create e2e"); expect(orgCreateStdout).toContain("Created organization"); + const { stdout: orgCreateService } = await execAsync("rill service create e2e"); + expect(orgCreateService).toContain("Created service"); + + const embedToken = orgCreateService.match(/Access token:\s+(\S+)/); + console.log("Embed token:", embedToken![1]); + writeFileEnsuringDir(RILL_EMBED_TOKEN, embedToken![1]); + // Go to the organization's page await page.goto("/e2e"); await expect(page.getByRole("heading", { name: "e2e" })).toBeVisible(); @@ -143,11 +151,12 @@ setup( "rill-openrtb-prog-ads", "--project", "openrtb", - "--github", + "--upload", ], /https?:\/\/[^\s]+/, ); + /* // Navigate to the GitHub auth URL // (In a fresh browser, this would typically trigger a log-in to GitHub, but we've bootstrapped the Playwright browser with GitHub auth cookies. // See the `save-github-cookies` project in `playwright.config.ts` for details.) @@ -159,6 +168,7 @@ setup( // TODO: Replace this with a better check. Maybe we could modify `spawnAndMatch` to match an array of regexes. await page.waitForTimeout(10000); + */ // Expect to see the successful deployment await page.goto("/e2e/openrtb"); await expect(page.getByText("Your trial expires in 30 days")).toBeVisible(); // Billing banner From fbbe87a3cb29b249a3aaeeab31186355f9629f36 Mon Sep 17 00:00:00 2001 From: Alexander Thor Date: Mon, 17 Feb 2025 10:27:42 +0100 Subject: [PATCH 10/37] Revert "fix: first stub of tests" This reverts commit 2e3308232c5d628da04b862303b04e11337f568f. --- web-admin/playwright.config.ts | 18 +++++++++--------- web-admin/tests/setup/constants.ts | 2 -- web-admin/tests/setup/github-session-setup.ts | 4 ---- web-admin/tests/setup/setup.ts | 14 ++------------ 4 files changed, 11 insertions(+), 27 deletions(-) diff --git a/web-admin/playwright.config.ts b/web-admin/playwright.config.ts index 4baafbb5610..74da3980ff8 100644 --- a/web-admin/playwright.config.ts +++ b/web-admin/playwright.config.ts @@ -18,7 +18,7 @@ const config: PlaywrightTestConfig = { }, testDir: "tests", projects: [ - /* ...(process.env.CI + ...(process.env.CI ? [] // skip in CI : [ { @@ -29,7 +29,7 @@ const config: PlaywrightTestConfig = { name: "setup-github-session", testMatch: "github-session-setup.ts", }, - ]),*/ + ]), { name: "setup", testMatch: "setup.ts", @@ -40,14 +40,14 @@ const config: PlaywrightTestConfig = { ...(process.env.CI ? [] // skip in CI, since the GitHub Action uses an ephemeral runner : [ - { - name: "teardown", - testMatch: "teardown.ts", - use: { - storageState: ADMIN_STORAGE_STATE, + { + name: "teardown", + testMatch: "teardown.ts", + use: { + storageState: ADMIN_STORAGE_STATE, + }, }, - }, - ]), + ]), { name: "e2e", dependencies: process.env.E2E_NO_SETUP_OR_TEARDOWN ? [] : ["setup"], diff --git a/web-admin/tests/setup/constants.ts b/web-admin/tests/setup/constants.ts index 471c65fed76..34c0b03e26d 100644 --- a/web-admin/tests/setup/constants.ts +++ b/web-admin/tests/setup/constants.ts @@ -4,5 +4,3 @@ export const VIEWER_STORAGE_STATE = "playwright/.auth/viewer.json"; export const RILL_DEVTOOL_BACKGROUND_PROCESS_PID_FILE = "playwright/rill-devtool-background-process-pid.txt"; - -export const RILL_EMBED_TOKEN = "playwright/rill-embed-token.txt"; \ No newline at end of file diff --git a/web-admin/tests/setup/github-session-setup.ts b/web-admin/tests/setup/github-session-setup.ts index 34f7a1e78dc..a05d8723fc0 100644 --- a/web-admin/tests/setup/github-session-setup.ts +++ b/web-admin/tests/setup/github-session-setup.ts @@ -5,7 +5,6 @@ import { updateEnvVariable } from "../utils/dotenv"; import { execAsync } from "../utils/spawn"; import { test as setup } from "./base"; import { GITHUB_STORAGE_STATE } from "./constants"; -import dotenv from "dotenv"; const githubEnvVarName = "RILL_DEVTOOL_E2E_GITHUB_STORAGE_STATE_JSON"; @@ -15,9 +14,6 @@ const envFilePath = path.resolve(__dirname, "../../../.env"); setup( "should authenticate to GitHub and save the session", async ({ page }) => { - - // Load environment variables from our root `.env` file - dotenv.config({ path: envFilePath }); if ( !process.env.RILL_DEVTOOL_E2E_ADMIN_ACCOUNT_EMAIL || !process.env.RILL_DEVTOOL_E2E_ADMIN_ACCOUNT_PASSWORD diff --git a/web-admin/tests/setup/setup.ts b/web-admin/tests/setup/setup.ts index 8ee3c0799c0..ad243d22c1b 100644 --- a/web-admin/tests/setup/setup.ts +++ b/web-admin/tests/setup/setup.ts @@ -11,7 +11,6 @@ import { test as setup } from "./base"; import { ADMIN_STORAGE_STATE, RILL_DEVTOOL_BACKGROUND_PROCESS_PID_FILE, - RILL_EMBED_TOKEN, } from "./constants"; import { cliLogin } from "./fixtures/cli"; @@ -28,7 +27,7 @@ setup( // Start the cloud dependencies via Docker // This will block until the services are ready await spawnAndMatch( - "./rill", + "rill", ["devtool", "start", "e2e", "--reset", "--only", "deps"], /All services ready/, { @@ -129,13 +128,6 @@ setup( const { stdout: orgCreateStdout } = await execAsync("rill org create e2e"); expect(orgCreateStdout).toContain("Created organization"); - const { stdout: orgCreateService } = await execAsync("rill service create e2e"); - expect(orgCreateService).toContain("Created service"); - - const embedToken = orgCreateService.match(/Access token:\s+(\S+)/); - console.log("Embed token:", embedToken![1]); - writeFileEnsuringDir(RILL_EMBED_TOKEN, embedToken![1]); - // Go to the organization's page await page.goto("/e2e"); await expect(page.getByRole("heading", { name: "e2e" })).toBeVisible(); @@ -151,12 +143,11 @@ setup( "rill-openrtb-prog-ads", "--project", "openrtb", - "--upload", + "--github", ], /https?:\/\/[^\s]+/, ); - /* // Navigate to the GitHub auth URL // (In a fresh browser, this would typically trigger a log-in to GitHub, but we've bootstrapped the Playwright browser with GitHub auth cookies. // See the `save-github-cookies` project in `playwright.config.ts` for details.) @@ -168,7 +159,6 @@ setup( // TODO: Replace this with a better check. Maybe we could modify `spawnAndMatch` to match an array of regexes. await page.waitForTimeout(10000); - */ // Expect to see the successful deployment await page.goto("/e2e/openrtb"); await expect(page.getByText("Your trial expires in 30 days")).toBeVisible(); // Billing banner From b002ef5dd06d0b28a793ef2bf958992b38899ac5 Mon Sep 17 00:00:00 2001 From: Alexander Thor Date: Mon, 17 Feb 2025 11:10:28 +0100 Subject: [PATCH 11/37] fix: clear method names --- .../src/features/embeds/EmbedPublicAPI.svelte | 11 +- web-admin/tests/setup/git/repos/rill-examples | 1 + web-common/src/lib/rpc.ts | 121 +++++++++--------- 3 files changed, 68 insertions(+), 65 deletions(-) create mode 160000 web-admin/tests/setup/git/repos/rill-examples diff --git a/web-admin/src/features/embeds/EmbedPublicAPI.svelte b/web-admin/src/features/embeds/EmbedPublicAPI.svelte index c032866fc79..f8e62a4a39a 100644 --- a/web-admin/src/features/embeds/EmbedPublicAPI.svelte +++ b/web-admin/src/features/embeds/EmbedPublicAPI.svelte @@ -12,7 +12,10 @@ getTimeControlState, type TimeControlState, } from "@rilldata/web-common/features/dashboards/time-controls/time-control-store"; - import { registerMethod, emit } from "@rilldata/web-common/lib/rpc"; + import { + registerRPCMethod, + emitNotification, + } from "@rilldata/web-common/lib/rpc"; export let instanceId: string; @@ -47,7 +50,7 @@ ); } - registerMethod("getState", () => { + registerRPCMethod("getState", () => { return { state: decodeURIComponent( convertExploreStateToURLSearchParams( @@ -60,7 +63,7 @@ }; }); - registerMethod("setState", (state: string) => { + registerRPCMethod("setState", (state: string) => { const currentUrl = new URL(get(page).url); currentUrl.search = state; goto(currentUrl, { replaceState: true }); @@ -77,7 +80,7 @@ ); // Stream the state to the parent - $: emit("stateChange", { + $: emitNotification("stateChange", { state: stateString, }); diff --git a/web-admin/tests/setup/git/repos/rill-examples b/web-admin/tests/setup/git/repos/rill-examples new file mode 160000 index 00000000000..5b0b6fdd5e1 --- /dev/null +++ b/web-admin/tests/setup/git/repos/rill-examples @@ -0,0 +1 @@ +Subproject commit 5b0b6fdd5e13d73743dabbb2c96d09a8e1c46091 diff --git a/web-common/src/lib/rpc.ts b/web-common/src/lib/rpc.ts index 43c67961bee..971fcf348c7 100644 --- a/web-common/src/lib/rpc.ts +++ b/web-common/src/lib/rpc.ts @@ -1,98 +1,97 @@ type JSONRPCRequest = { - id?: string | number | null; - method: string; - params?: unknown; + id?: string | number | null; + method: string; + params?: unknown; }; type JSONRPCResponse = { - id?: string | number | null; - result?: unknown; - error?: { - code: number; - message: string; - data?: unknown; - }; + id?: string | number | null; + result?: unknown; + error?: { + code: number; + message: string; + data?: unknown; + }; }; const JSONRPC_ERRORS = { - PARSE_ERROR: { code: -32700, message: "Parse error" }, - INVALID_REQUEST: { code: -32600, message: "Invalid Request" }, - METHOD_NOT_FOUND: { code: -32601, message: "Method not found" }, - INVALID_PARAMS: { code: -32602, message: "Invalid params" }, - INTERNAL_ERROR: { code: -32603, message: "Internal error" }, + PARSE_ERROR: { code: -32700, message: "Parse error" }, + INVALID_REQUEST: { code: -32600, message: "Invalid Request" }, + METHOD_NOT_FOUND: { code: -32601, message: "Method not found" }, + INVALID_PARAMS: { code: -32602, message: "Invalid params" }, + INTERNAL_ERROR: { code: -32603, message: "Internal error" }, }; type JSONRPCMethods = { - [key: string]: (params?: unknown) => Promise | unknown; + [key: string]: (params?: unknown) => Promise | unknown; }; const methods: JSONRPCMethods = { - echo(message: { message: string }) { - return message; - }, + echo(message: { message: string }) { + return message; + }, }; async function handleRPCMessage(event: MessageEvent) { - if (typeof event.data !== "object" || event.data === null) { - return sendError(null, JSONRPC_ERRORS.INVALID_REQUEST); - } + if (typeof event.data !== "object" || event.data === null) { + return sendError(null, JSONRPC_ERRORS.INVALID_REQUEST); + } - const { id = null, method, params } = event.data; + const { id = null, method, params } = event.data; - if (typeof method !== "string") { - return sendError(id, JSONRPC_ERRORS.INVALID_REQUEST); - } + if (typeof method !== "string") { + return sendError(id, JSONRPC_ERRORS.INVALID_REQUEST); + } - if (!methods[method]) { - return sendError(id, JSONRPC_ERRORS.METHOD_NOT_FOUND); - } + if (!methods[method]) { + return sendError(id, JSONRPC_ERRORS.METHOD_NOT_FOUND); + } - try { - const result = await methods[method](params); - if (id !== null) { - sendResponse(id, result); - } - } catch (error) { - sendError(id, { - code: JSONRPC_ERRORS.INTERNAL_ERROR.code, - message: (error as Error).message, - }); + try { + const result = await methods[method](params); + if (id !== null) { + sendResponse(id, result); } + } catch (error) { + sendError(id, { + code: JSONRPC_ERRORS.INTERNAL_ERROR.code, + message: (error as Error).message, + }); + } } function sendResponse(id: string | number | null, result: unknown) { - if (window.parent !== window) { - window.parent.postMessage({ id, result } as JSONRPCResponse, "*"); - } + if (window.parent !== window) { + window.parent.postMessage({ id, result } as JSONRPCResponse, "*"); + } } function sendError( - id: string | number | null, - error: { code: number; message: string; data?: unknown }, + id: string | number | null, + error: { code: number; message: string; data?: unknown }, ) { - if (window.parent !== window) { - window.parent.postMessage({ id, error } as JSONRPCResponse, "*"); - } + if (window.parent !== window) { + window.parent.postMessage({ id, error } as JSONRPCResponse, "*"); + } } export function initRPC() { - window.removeEventListener("message", (_event: MessageEvent) => { }); - window.addEventListener("message", (event: MessageEvent) => { - if (event.source && event.data) { - void handleRPCMessage(event as MessageEvent); - } - }); + window.addEventListener("message", (event: MessageEvent) => { + if (event.source && event.data) { + void handleRPCMessage(event as MessageEvent); + } + }); } -export function registerMethod( - name: string, - func: (params: T) => Promise | unknown, +export function registerRPCMethod( + name: string, + func: (params: T) => Promise | unknown, ) { - methods[name] = func; + methods[name] = func; } -export function emit(method: string, params?: unknown) { - if (window.parent !== window) { - window.parent.postMessage({ method, params } as JSONRPCRequest, "*"); - } +export function emitNotification(method: string, params?: unknown) { + if (window.parent !== window) { + window.parent.postMessage({ method, params } as JSONRPCRequest, "*"); + } } From 880fd04afaa5a096397f2c59e6868f3b197c1e5d Mon Sep 17 00:00:00 2001 From: Alexander Thor Date: Mon, 17 Feb 2025 11:33:03 +0100 Subject: [PATCH 12/37] fix: check input --- web-admin/src/features/embeds/EmbedPublicAPI.svelte | 3 +++ 1 file changed, 3 insertions(+) diff --git a/web-admin/src/features/embeds/EmbedPublicAPI.svelte b/web-admin/src/features/embeds/EmbedPublicAPI.svelte index f8e62a4a39a..7ba3b0a2b18 100644 --- a/web-admin/src/features/embeds/EmbedPublicAPI.svelte +++ b/web-admin/src/features/embeds/EmbedPublicAPI.svelte @@ -64,6 +64,9 @@ }); registerRPCMethod("setState", (state: string) => { + if (typeof state !== "string") { + return new Error("Expected state to be a string"); + } const currentUrl = new URL(get(page).url); currentUrl.search = state; goto(currentUrl, { replaceState: true }); From ec97c8e983bdf8eb47a7023d65de7df2d9594786 Mon Sep 17 00:00:00 2001 From: Alexander Thor Date: Mon, 17 Feb 2025 17:17:43 +0100 Subject: [PATCH 13/37] fix: gitignore --- web-admin/.gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/web-admin/.gitignore b/web-admin/.gitignore index 0df9c1f6145..cb89335d146 100644 --- a/web-admin/.gitignore +++ b/web-admin/.gitignore @@ -1,2 +1,4 @@ /.svelte-kit -build \ No newline at end of file +build +tests/embed.html +tests/set/git/* \ No newline at end of file From 46ee2b35b1f9f24ea1db10358e43cf3fef9269b3 Mon Sep 17 00:00:00 2001 From: Alexander Thor Date: Mon, 17 Feb 2025 17:24:44 +0100 Subject: [PATCH 14/37] fix: ignore --- web-admin/.gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web-admin/.gitignore b/web-admin/.gitignore index cb89335d146..4dd44352dc7 100644 --- a/web-admin/.gitignore +++ b/web-admin/.gitignore @@ -1,4 +1,4 @@ /.svelte-kit build tests/embed.html -tests/set/git/* \ No newline at end of file +tests/setup/git/ \ No newline at end of file From 92aa0b23ad2609ca295e3d13431a94223a12d7a1 Mon Sep 17 00:00:00 2001 From: Alexander Thor Date: Mon, 17 Feb 2025 17:25:57 +0100 Subject: [PATCH 15/37] Revert "fix: ignore" This reverts commit 46ee2b35b1f9f24ea1db10358e43cf3fef9269b3. --- web-admin/.gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web-admin/.gitignore b/web-admin/.gitignore index 4dd44352dc7..cb89335d146 100644 --- a/web-admin/.gitignore +++ b/web-admin/.gitignore @@ -1,4 +1,4 @@ /.svelte-kit build tests/embed.html -tests/setup/git/ \ No newline at end of file +tests/set/git/* \ No newline at end of file From 6fc5e761757971d5f1e414507ccd9c64877e158d Mon Sep 17 00:00:00 2001 From: Alexander Thor Date: Mon, 17 Feb 2025 17:26:03 +0100 Subject: [PATCH 16/37] Revert "fix: gitignore" This reverts commit ec97c8e983bdf8eb47a7023d65de7df2d9594786. --- web-admin/.gitignore | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/web-admin/.gitignore b/web-admin/.gitignore index cb89335d146..0df9c1f6145 100644 --- a/web-admin/.gitignore +++ b/web-admin/.gitignore @@ -1,4 +1,2 @@ /.svelte-kit -build -tests/embed.html -tests/set/git/* \ No newline at end of file +build \ No newline at end of file From 008d5b83a4fbc73137a731f98862fd9c35917f3f Mon Sep 17 00:00:00 2001 From: Alexander Thor Date: Mon, 17 Feb 2025 19:24:02 +0100 Subject: [PATCH 17/37] fix: add tests --- web-admin/tests/embeds.spec.ts | 131 +++++++++++++++++++++++++ web-admin/tests/setup/base.ts | 17 ++++ web-admin/tests/setup/constants.ts | 1 + web-admin/tests/setup/setup.ts | 13 +++ web-admin/tests/utils/generateEmbed.ts | 59 +++++++++++ 5 files changed, 221 insertions(+) create mode 100644 web-admin/tests/embeds.spec.ts create mode 100644 web-admin/tests/utils/generateEmbed.ts diff --git a/web-admin/tests/embeds.spec.ts b/web-admin/tests/embeds.spec.ts new file mode 100644 index 00000000000..cd28ec69810 --- /dev/null +++ b/web-admin/tests/embeds.spec.ts @@ -0,0 +1,131 @@ +import { expect } from "@playwright/test"; +import { test } from "./setup/base"; + +test.describe("Embeds", () => { + test("embeds should load", async ({ embedPage }) => { + const frame = embedPage.frameLocator("iframe"); + // Set the time zone to UTC + await frame.getByLabel("Timezone selector").click(); + await frame.getByRole("menuitem", { name: "UTC GMT +00:00 UTC" }).click(); + + // Check the Big Number + await expect( + frame.getByRole("button", { name: "Advertising Spend Overall $1.30M" }), + ).toBeVisible(); + }); + + test("state is emitted for embeds", async ({ embedPage }) => { + const logMessages: string[] = []; + + // Listen to console messages + embedPage.on("console", async (msg) => { + if (msg.type() === "log") { + const args = await Promise.all( + msg.args().map((arg) => arg.jsonValue()), + ); + logMessages.push(JSON.stringify(args)); + } + }); + + const frame = embedPage.frameLocator("iframe"); + + // Set the time zone to UTC + await frame.getByLabel("Timezone selector").click(); + await frame.getByRole("menuitem", { name: "UTC GMT +00:00 UTC" }).click(); + + // Click a dimension row + await frame.getByRole("row", { name: "Instacart $107.3k" }).click(); + await embedPage.waitForTimeout(500); + + expect( + logMessages.some((msg) => + msg.includes("tz=UTC&f=advertiser_name+IN+('Instacart')"), + ), + ).toBeTruthy(); + }); + + test("getState returns from embed", async ({ embedPage }) => { + const logMessages: string[] = []; + + // Listen to console messages + embedPage.on("console", async (msg) => { + if (msg.type() === "log") { + const args = await Promise.all( + msg.args().map((arg) => arg.jsonValue()), + ); + logMessages.push(JSON.stringify(args)); + } + }); + + const frame = embedPage.frameLocator("iframe"); + + // Set the time zone to UTC + await frame.getByLabel("Timezone selector").click(); + await frame.getByRole("menuitem", { name: "UTC GMT +00:00 UTC" }).click(); + + // Click a dimension row + await frame.getByRole("row", { name: "Instacart $107.3k" }).click(); + await embedPage.waitForTimeout(500); + + await embedPage.evaluate(() => { + const iframe = document.querySelector("iframe"); + if (iframe) { + if (iframe.contentWindow) { + iframe.contentWindow.postMessage( + { id: 1337, method: "getState" }, + "*", + ); + } + } + }); + await embedPage.waitForTimeout(500); + expect( + logMessages.some((msg) => + msg.includes( + `{"id":1337,"result":{"state":"tz=UTC&f=advertiser_name+IN+('Instacart')"}}`, + ), + ), + ).toBeTruthy(); + }); + + test("setState changes embedded explore", async ({ embedPage }) => { + const logMessages: string[] = []; + + // Listen to console messages + embedPage.on("console", async (msg) => { + if (msg.type() === "log") { + const args = await Promise.all( + msg.args().map((arg) => arg.jsonValue()), + ); + logMessages.push(JSON.stringify(args)); + } + }); + + const frame = embedPage.frameLocator("iframe"); + + await embedPage.evaluate(() => { + const iframe = + window.document.getElementsByTagName("iframe")[0].contentWindow; + if (iframe) { + iframe.postMessage( + { + id: 1337, + method: "setState", + params: "tz=UTC&f=advertiser_name+IN+('Instacart')", + }, + "*", + ); + } + }); + + await embedPage.waitForTimeout(500); + + await expect(frame.getByLabel("Timezone selector")).toHaveText("UTC"); + await expect( + frame.getByRole("row", { name: "Instacart $107.3k" }), + ).toBeVisible(); + expect( + logMessages.some((msg) => msg.includes(`{"id":1337,"result":true}`)), + ).toBeTruthy(); + }); +}); diff --git a/web-admin/tests/setup/base.ts b/web-admin/tests/setup/base.ts index 6367873d0e3..889d58a1710 100644 --- a/web-admin/tests/setup/base.ts +++ b/web-admin/tests/setup/base.ts @@ -1,12 +1,15 @@ import { test as base, type Page } from "@playwright/test"; import { ADMIN_STORAGE_STATE, VIEWER_STORAGE_STATE } from "./constants"; import { cliLogin, cliLogout } from "./fixtures/cli"; +import path from "path"; +import { fileURLToPath } from "url"; type MyFixtures = { adminPage: Page; viewerPage: Page; anonPage: Page; cli: void; + embedPage: Page; }; export const test = base.extend({ @@ -44,4 +47,18 @@ export const test = base.extend({ await use(); await cliLogout(); }, + + embedPage: async ({ browser }, use) => { + const __dirname = path.dirname(fileURLToPath(import.meta.url)); + const filePath = "file://" + path.resolve(__dirname, "..", "embed.html"); + + const context = await browser.newContext(); + const embedPage = await context.newPage(); + await embedPage.goto(filePath); + await embedPage.waitForTimeout(500); + + await use(embedPage); + + await context.close(); + }, }); diff --git a/web-admin/tests/setup/constants.ts b/web-admin/tests/setup/constants.ts index 34c0b03e26d..72241539cb2 100644 --- a/web-admin/tests/setup/constants.ts +++ b/web-admin/tests/setup/constants.ts @@ -4,3 +4,4 @@ export const VIEWER_STORAGE_STATE = "playwright/.auth/viewer.json"; export const RILL_DEVTOOL_BACKGROUND_PROCESS_PID_FILE = "playwright/rill-devtool-background-process-pid.txt"; +export const RILL_EMBED_SERVICE_TOKEN = "playwright/rill-service-token.txt"; diff --git a/web-admin/tests/setup/setup.ts b/web-admin/tests/setup/setup.ts index ad243d22c1b..a34ba2deda8 100644 --- a/web-admin/tests/setup/setup.ts +++ b/web-admin/tests/setup/setup.ts @@ -5,12 +5,14 @@ import dotenv from "dotenv"; import path from "path"; import { fileURLToPath } from "url"; import { writeFileEnsuringDir } from "../utils/fs"; +import { generateEmbed } from "../utils/generateEmbed"; import { execAsync, spawnAndMatch } from "../utils/spawn"; import type { StorageState } from "../utils/storage-state"; import { test as setup } from "./base"; import { ADMIN_STORAGE_STATE, RILL_DEVTOOL_BACKGROUND_PROCESS_PID_FILE, + RILL_EMBED_SERVICE_TOKEN, } from "./constants"; import { cliLogin } from "./fixtures/cli"; @@ -128,6 +130,13 @@ setup( const { stdout: orgCreateStdout } = await execAsync("rill org create e2e"); expect(orgCreateStdout).toContain("Created organization"); + const { stdout: orgCreateService } = await execAsync( + "rill service create e2e", + ); + expect(orgCreateService).toContain("Created service"); + + const embedToken = orgCreateService.match(/Access token:\s+(\S+)/); + // Go to the organization's page await page.goto("/e2e"); await expect(page.getByRole("heading", { name: "e2e" })).toBeVisible(); @@ -187,6 +196,10 @@ setup( { intervals: Array(24).fill(5_000), timeout: 180_000 }, ) .toContain("Last refreshed"); + + // generate a embed file + writeFileEnsuringDir(RILL_EMBED_SERVICE_TOKEN, embedToken![1]); + await generateEmbed("bids_explore", embedToken![1]); }, ); diff --git a/web-admin/tests/utils/generateEmbed.ts b/web-admin/tests/utils/generateEmbed.ts new file mode 100644 index 00000000000..3785af9d2ec --- /dev/null +++ b/web-admin/tests/utils/generateEmbed.ts @@ -0,0 +1,59 @@ +import type { AxiosResponse } from "axios"; +import axios from "axios"; +import fs from "fs"; +import path from "path"; +import { fileURLToPath } from "url"; + +export async function generateEmbed( + resourceId: string, + serviceToken: string, +): Promise { + try { + const response: AxiosResponse<{ iframeSrc: string }> = await axios.post( + "http://localhost:8080/v1/organizations/e2e/projects/openrtb/iframe", + { + resource: resourceId, + navigation: true, + }, + { + headers: { + Authorization: `Bearer ${serviceToken}`, + "Content-Type": "application/json", + }, + }, + ); + + const iframeSrc = response.data.iframeSrc; + if (!iframeSrc) { + throw new Error("Invalid response: iframeSrc not found"); + } + + const htmlContent = ` + + + + + Iframe Example + + + + + +`; + + const __dirname = path.dirname(fileURLToPath(import.meta.url)); + const outputPath = path.join(__dirname, "..", "embed.html"); + console.log(outputPath); + fs.writeFileSync(outputPath, htmlContent, "utf8"); + } catch (error: unknown) { + if (error instanceof Error) { + console.error("Error fetching iframe or saving file:", error.message); + } else { + console.error("An unknown error occurred:", error); + } + } +} From 1010941c926cb39f4b21be0aa6019a80fedb5d56 Mon Sep 17 00:00:00 2001 From: Alexander Thor Date: Mon, 17 Feb 2025 23:02:06 +0100 Subject: [PATCH 18/37] Merge remote-tracking branch 'origin/main' into feat/emit-state-embed From 2e0c3959265d5b0b70c82e069aaaab98f0272445 Mon Sep 17 00:00:00 2001 From: Alexander Thor Date: Tue, 18 Feb 2025 16:04:46 +0100 Subject: [PATCH 19/37] fix: comments --- web-admin/tests/setup/git/repos/rill-examples | 1 - web-admin/tests/setup/setup.ts | 5 +++-- .../utils/{generateEmbed.ts => generate-embed.ts} | 0 web-common/src/lib/rpc.ts | 13 ++++++++++--- 4 files changed, 13 insertions(+), 6 deletions(-) delete mode 160000 web-admin/tests/setup/git/repos/rill-examples rename web-admin/tests/utils/{generateEmbed.ts => generate-embed.ts} (100%) diff --git a/web-admin/tests/setup/git/repos/rill-examples b/web-admin/tests/setup/git/repos/rill-examples deleted file mode 160000 index 5b0b6fdd5e1..00000000000 --- a/web-admin/tests/setup/git/repos/rill-examples +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 5b0b6fdd5e13d73743dabbb2c96d09a8e1c46091 diff --git a/web-admin/tests/setup/setup.ts b/web-admin/tests/setup/setup.ts index 705369f24e8..b7cc5368658 100644 --- a/web-admin/tests/setup/setup.ts +++ b/web-admin/tests/setup/setup.ts @@ -5,7 +5,7 @@ import dotenv from "dotenv"; import path from "path"; import { fileURLToPath } from "url"; import { writeFileEnsuringDir } from "../utils/fs"; -import { generateEmbed } from "../utils/generateEmbed"; +import { generateEmbed } from "../utils/generate-embed"; import { execAsync, spawnAndMatch } from "../utils/spawn"; import type { StorageState } from "../utils/storage-state"; import { test as setup } from "./base"; @@ -116,6 +116,8 @@ setup( .locator('input[name="username"]') .fill(process.env.RILL_DEVTOOL_E2E_ADMIN_ACCOUNT_EMAIL); + await page.getByRole('button', { name: 'Continue', exact: true }).click(); + // Select and fill in the password await page.locator('input[name="password"]').click(); await page @@ -124,7 +126,6 @@ setup( // Click the continue button await page.getByRole("button", { name: "Continue" }).click(); - await page.waitForURL("/"); // Save auth cookies to file diff --git a/web-admin/tests/utils/generateEmbed.ts b/web-admin/tests/utils/generate-embed.ts similarity index 100% rename from web-admin/tests/utils/generateEmbed.ts rename to web-admin/tests/utils/generate-embed.ts diff --git a/web-common/src/lib/rpc.ts b/web-common/src/lib/rpc.ts index 971fcf348c7..24942cff615 100644 --- a/web-common/src/lib/rpc.ts +++ b/web-common/src/lib/rpc.ts @@ -53,9 +53,10 @@ async function handleRPCMessage(event: MessageEvent) { sendResponse(id, result); } } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); sendError(id, { code: JSONRPC_ERRORS.INTERNAL_ERROR.code, - message: (error as Error).message, + message: errorMessage, }); } } @@ -76,11 +77,17 @@ function sendError( } export function initRPC() { - window.addEventListener("message", (event: MessageEvent) => { + const handler = (event: MessageEvent) => { if (event.source && event.data) { void handleRPCMessage(event as MessageEvent); } - }); + }; + + window.addEventListener("message", handler); + + return () => { + window.removeEventListener("message", handler); + }; } export function registerRPCMethod( From 1f40902f4f4ca3d14ed418aab9646bc60d58186f Mon Sep 17 00:00:00 2001 From: Alexander Thor Date: Tue, 18 Feb 2025 16:13:38 +0100 Subject: [PATCH 20/37] fix: moving org and project to const --- web-admin/tests/setup/constants.ts | 3 +++ web-admin/tests/setup/setup.ts | 21 ++++++++++++--------- web-admin/tests/utils/generate-embed.ts | 4 +++- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/web-admin/tests/setup/constants.ts b/web-admin/tests/setup/constants.ts index 72241539cb2..d32f009b650 100644 --- a/web-admin/tests/setup/constants.ts +++ b/web-admin/tests/setup/constants.ts @@ -5,3 +5,6 @@ export const VIEWER_STORAGE_STATE = "playwright/.auth/viewer.json"; export const RILL_DEVTOOL_BACKGROUND_PROCESS_PID_FILE = "playwright/rill-devtool-background-process-pid.txt"; export const RILL_EMBED_SERVICE_TOKEN = "playwright/rill-service-token.txt"; +export const RILL_ORG_NAME = "e2e"; +export const RILL_PROJECT_NAME = "openrtb"; +export const RILL_SERVICE_NAME = "e2e"; diff --git a/web-admin/tests/setup/setup.ts b/web-admin/tests/setup/setup.ts index b7cc5368658..a01cf618bbb 100644 --- a/web-admin/tests/setup/setup.ts +++ b/web-admin/tests/setup/setup.ts @@ -13,6 +13,9 @@ import { ADMIN_STORAGE_STATE, RILL_DEVTOOL_BACKGROUND_PROCESS_PID_FILE, RILL_EMBED_SERVICE_TOKEN, + RILL_ORG_NAME, + RILL_PROJECT_NAME, + RILL_SERVICE_NAME, } from "./constants"; import { cliLogin } from "./fixtures/cli"; @@ -134,19 +137,19 @@ setup( // Create an organization named "e2e" await cliLogin(page); - const { stdout: orgCreateStdout } = await execAsync("rill org create e2e"); + const { stdout: orgCreateStdout } = await execAsync(`rill org create ${RILL_ORG_NAME}`); expect(orgCreateStdout).toContain("Created organization"); const { stdout: orgCreateService } = await execAsync( - "rill service create e2e", + `rill service create ${RILL_SERVICE_NAME}`, ); expect(orgCreateService).toContain("Created service"); const embedToken = orgCreateService.match(/Access token:\s+(\S+)/); // Go to the organization's page - await page.goto("/e2e"); - await expect(page.getByRole("heading", { name: "e2e" })).toBeVisible(); + await page.goto(`/${RILL_ORG_NAME}`); + await expect(page.getByRole("heading", { name: RILL_ORG_NAME })).toBeVisible(); // Deploy the OpenRTB project const { match } = await spawnAndMatch( @@ -158,7 +161,7 @@ setup( "--subpath", "rill-openrtb-prog-ads", "--project", - "openrtb", + RILL_PROJECT_NAME, "--github", ], /https?:\/\/[^\s]+/, @@ -176,11 +179,11 @@ setup( await page.waitForTimeout(10000); // Expect to see the successful deployment - await page.goto("/e2e/openrtb"); + await page.goto(`/${RILL_ORG_NAME}/${RILL_PROJECT_NAME}`); await expect(page.getByText("Your trial expires in 30 days")).toBeVisible(); // Billing banner - await expect(page.getByText("e2e")).toBeVisible(); // Organization breadcrumb + await expect(page.getByText(RILL_ORG_NAME)).toBeVisible(); // Organization breadcrumb await expect(page.getByText("Free trial")).toBeVisible(); // Billing status - await expect(page.getByText("openrtb")).toBeVisible(); // Project breadcrumb + await expect(page.getByText(RILL_PROJECT_NAME)).toBeVisible(); // Project breadcrumb // Check that the dashboards are listed await expect( @@ -206,7 +209,7 @@ setup( // generate a embed file writeFileEnsuringDir(RILL_EMBED_SERVICE_TOKEN, embedToken![1]); - await generateEmbed("bids_explore", embedToken![1]); + await generateEmbed("bids_explore", embedToken![1], RILL_ORG_NAME, RILL_PROJECT_NAME); }, ); diff --git a/web-admin/tests/utils/generate-embed.ts b/web-admin/tests/utils/generate-embed.ts index 3785af9d2ec..4beb94f30c5 100644 --- a/web-admin/tests/utils/generate-embed.ts +++ b/web-admin/tests/utils/generate-embed.ts @@ -7,10 +7,12 @@ import { fileURLToPath } from "url"; export async function generateEmbed( resourceId: string, serviceToken: string, + organization: string, + project: string, ): Promise { try { const response: AxiosResponse<{ iframeSrc: string }> = await axios.post( - "http://localhost:8080/v1/organizations/e2e/projects/openrtb/iframe", + `http://localhost:8080/v1/${organization}/e2e/projects/${project}/iframe`, { resource: resourceId, navigation: true, From cbb065596a73849b71fefd48f38f535ef46e4b61 Mon Sep 17 00:00:00 2001 From: Alexander Thor Date: Tue, 18 Feb 2025 16:59:45 +0100 Subject: [PATCH 21/37] fix: moving embed to test hook --- web-admin/tests/embeds.spec.ts | 21 +++++++++++++++++++++ web-admin/tests/setup/setup.ts | 17 ++++++++++++----- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/web-admin/tests/embeds.spec.ts b/web-admin/tests/embeds.spec.ts index cd28ec69810..6ddcf6b5ac5 100644 --- a/web-admin/tests/embeds.spec.ts +++ b/web-admin/tests/embeds.spec.ts @@ -1,5 +1,26 @@ import { expect } from "@playwright/test"; import { test } from "./setup/base"; +import { generateEmbed } from "./utils/generate-embed"; +import * as fs from "fs"; +import * as path from "path"; +import { RILL_ORG_NAME, RILL_PROJECT_NAME } from "./setup/constants"; +import { fileURLToPath } from "url"; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const rillServiceToken = fs.readFileSync( + path.resolve(__dirname, "rill-service-token.txt"), + "utf-8", +); + + +test.beforeAll(async () => { + await generateEmbed( + "bids_explore", + rillServiceToken, + RILL_ORG_NAME, + RILL_PROJECT_NAME, + ); +}); test.describe("Embeds", () => { test("embeds should load", async ({ embedPage }) => { diff --git a/web-admin/tests/setup/setup.ts b/web-admin/tests/setup/setup.ts index a01cf618bbb..a286a70f797 100644 --- a/web-admin/tests/setup/setup.ts +++ b/web-admin/tests/setup/setup.ts @@ -119,8 +119,6 @@ setup( .locator('input[name="username"]') .fill(process.env.RILL_DEVTOOL_E2E_ADMIN_ACCOUNT_EMAIL); - await page.getByRole('button', { name: 'Continue', exact: true }).click(); - // Select and fill in the password await page.locator('input[name="password"]').click(); await page @@ -137,7 +135,9 @@ setup( // Create an organization named "e2e" await cliLogin(page); - const { stdout: orgCreateStdout } = await execAsync(`rill org create ${RILL_ORG_NAME}`); + const { stdout: orgCreateStdout } = await execAsync( + `rill org create ${RILL_ORG_NAME}`, + ); expect(orgCreateStdout).toContain("Created organization"); const { stdout: orgCreateService } = await execAsync( @@ -149,7 +149,9 @@ setup( // Go to the organization's page await page.goto(`/${RILL_ORG_NAME}`); - await expect(page.getByRole("heading", { name: RILL_ORG_NAME })).toBeVisible(); + await expect( + page.getByRole("heading", { name: RILL_ORG_NAME }), + ).toBeVisible(); // Deploy the OpenRTB project const { match } = await spawnAndMatch( @@ -209,7 +211,12 @@ setup( // generate a embed file writeFileEnsuringDir(RILL_EMBED_SERVICE_TOKEN, embedToken![1]); - await generateEmbed("bids_explore", embedToken![1], RILL_ORG_NAME, RILL_PROJECT_NAME); + await generateEmbed( + "bids_explore", + embedToken![1], + RILL_ORG_NAME, + RILL_PROJECT_NAME, + ); }, ); From 087287e902ecb17b8bf2e9e50904d45b46abda3f Mon Sep 17 00:00:00 2001 From: Alexander Thor Date: Tue, 18 Feb 2025 17:43:07 +0100 Subject: [PATCH 22/37] fix: format --- web-admin/tests/embeds.spec.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/web-admin/tests/embeds.spec.ts b/web-admin/tests/embeds.spec.ts index 6ddcf6b5ac5..8ad942f2d94 100644 --- a/web-admin/tests/embeds.spec.ts +++ b/web-admin/tests/embeds.spec.ts @@ -12,7 +12,6 @@ const rillServiceToken = fs.readFileSync( "utf-8", ); - test.beforeAll(async () => { await generateEmbed( "bids_explore", From 523f8aa5b889320d7b928195c43dbc35d9ee3296 Mon Sep 17 00:00:00 2001 From: Alexander Thor Date: Tue, 18 Feb 2025 18:24:28 +0100 Subject: [PATCH 23/37] fix: read token --- web-admin/tests/embeds.spec.ts | 16 +++++++--------- web-admin/tests/setup/setup.ts | 6 ------ 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/web-admin/tests/embeds.spec.ts b/web-admin/tests/embeds.spec.ts index 8ad942f2d94..835cc621487 100644 --- a/web-admin/tests/embeds.spec.ts +++ b/web-admin/tests/embeds.spec.ts @@ -2,15 +2,13 @@ import { expect } from "@playwright/test"; import { test } from "./setup/base"; import { generateEmbed } from "./utils/generate-embed"; import * as fs from "fs"; -import * as path from "path"; -import { RILL_ORG_NAME, RILL_PROJECT_NAME } from "./setup/constants"; -import { fileURLToPath } from "url"; - -const __dirname = path.dirname(fileURLToPath(import.meta.url)); -const rillServiceToken = fs.readFileSync( - path.resolve(__dirname, "rill-service-token.txt"), - "utf-8", -); +import { + RILL_ORG_NAME, + RILL_PROJECT_NAME, + RILL_EMBED_SERVICE_TOKEN, +} from "./setup/constants"; + +const rillServiceToken = fs.readFileSync(RILL_EMBED_SERVICE_TOKEN, "utf-8"); test.beforeAll(async () => { await generateEmbed( diff --git a/web-admin/tests/setup/setup.ts b/web-admin/tests/setup/setup.ts index 7e028bf0e60..7726831c207 100644 --- a/web-admin/tests/setup/setup.ts +++ b/web-admin/tests/setup/setup.ts @@ -206,12 +206,6 @@ setup( // generate a embed file writeFileEnsuringDir(RILL_EMBED_SERVICE_TOKEN, embedToken![1]); - await generateEmbed( - "bids_explore", - embedToken![1], - RILL_ORG_NAME, - RILL_PROJECT_NAME, - ); }, ); From e39c60dd074781ea0a6ae45e0e79d3b133bc1b24 Mon Sep 17 00:00:00 2001 From: Alexander Thor Date: Wed, 19 Feb 2025 16:08:51 +0100 Subject: [PATCH 24/37] fix: more fixes --- .../src/features/embeds/EmbedPublicAPI.svelte | 6 ++ web-admin/tests/embed.html | 16 ++++ web-admin/tests/embeds.spec.ts | 96 +++++++++---------- web-admin/tests/setup/base.ts | 16 +++- web-admin/tests/setup/setup.ts | 16 ++-- web-admin/tests/utils/generate-embed.ts | 5 +- 6 files changed, 92 insertions(+), 63 deletions(-) create mode 100644 web-admin/tests/embed.html diff --git a/web-admin/src/features/embeds/EmbedPublicAPI.svelte b/web-admin/src/features/embeds/EmbedPublicAPI.svelte index 7ba3b0a2b18..90346224100 100644 --- a/web-admin/src/features/embeds/EmbedPublicAPI.svelte +++ b/web-admin/src/features/embeds/EmbedPublicAPI.svelte @@ -2,6 +2,7 @@ import { goto } from "$app/navigation"; import { page } from "$app/stores"; import { get } from "svelte/store"; + import { onMount } from "svelte"; import { getStateManagers } from "@rilldata/web-common/features/dashboards/state-managers/state-managers"; import { convertExploreStateToURLSearchParams } from "@rilldata/web-common/features/dashboards/url-state/convertExploreStateToURLSearchParams"; @@ -64,6 +65,7 @@ }); registerRPCMethod("setState", (state: string) => { + console.log(state) if (typeof state !== "string") { return new Error("Expected state to be a string"); } @@ -86,6 +88,10 @@ $: emitNotification("stateChange", { state: stateString, }); + + onMount(() => { + emitNotification("ready"); + }); diff --git a/web-admin/tests/embed.html b/web-admin/tests/embed.html new file mode 100644 index 00000000000..35ce14648de --- /dev/null +++ b/web-admin/tests/embed.html @@ -0,0 +1,16 @@ + + + + + + Iframe Example + + + + + + \ No newline at end of file diff --git a/web-admin/tests/embeds.spec.ts b/web-admin/tests/embeds.spec.ts index 835cc621487..be40dc43adb 100644 --- a/web-admin/tests/embeds.spec.ts +++ b/web-admin/tests/embeds.spec.ts @@ -1,23 +1,5 @@ import { expect } from "@playwright/test"; import { test } from "./setup/base"; -import { generateEmbed } from "./utils/generate-embed"; -import * as fs from "fs"; -import { - RILL_ORG_NAME, - RILL_PROJECT_NAME, - RILL_EMBED_SERVICE_TOKEN, -} from "./setup/constants"; - -const rillServiceToken = fs.readFileSync(RILL_EMBED_SERVICE_TOKEN, "utf-8"); - -test.beforeAll(async () => { - await generateEmbed( - "bids_explore", - rillServiceToken, - RILL_ORG_NAME, - RILL_PROJECT_NAME, - ); -}); test.describe("Embeds", () => { test("embeds should load", async ({ embedPage }) => { @@ -35,16 +17,22 @@ test.describe("Embeds", () => { test("state is emitted for embeds", async ({ embedPage }) => { const logMessages: string[] = []; - // Listen to console messages - embedPage.on("console", async (msg) => { - if (msg.type() === "log") { - const args = await Promise.all( - msg.args().map((arg) => arg.jsonValue()), - ); - logMessages.push(JSON.stringify(args)); - } + const waitForReadyMessage = new Promise((resolve) => { + embedPage.on("console", async (msg) => { + if (msg.type() === "log") { + const args = await Promise.all(msg.args().map((arg) => arg.jsonValue())); + const logMessage = JSON.stringify(args); + logMessages.push(logMessage); + + if (logMessage.includes(`{"method":"ready"}`)) { + resolve(); + } + } + }); }); + await waitForReadyMessage; + const frame = embedPage.frameLocator("iframe"); // Set the time zone to UTC @@ -65,16 +53,22 @@ test.describe("Embeds", () => { test("getState returns from embed", async ({ embedPage }) => { const logMessages: string[] = []; - // Listen to console messages - embedPage.on("console", async (msg) => { - if (msg.type() === "log") { - const args = await Promise.all( - msg.args().map((arg) => arg.jsonValue()), - ); - logMessages.push(JSON.stringify(args)); - } + const waitForReadyMessage = new Promise((resolve) => { + embedPage.on("console", async (msg) => { + if (msg.type() === "log") { + const args = await Promise.all(msg.args().map((arg) => arg.jsonValue())); + const logMessage = JSON.stringify(args); + logMessages.push(logMessage); + + if (logMessage.includes(`{"method":"ready"}`)) { + resolve(); + } + } + }); }); + await waitForReadyMessage; + const frame = embedPage.frameLocator("iframe"); // Set the time zone to UTC @@ -109,16 +103,22 @@ test.describe("Embeds", () => { test("setState changes embedded explore", async ({ embedPage }) => { const logMessages: string[] = []; - // Listen to console messages - embedPage.on("console", async (msg) => { - if (msg.type() === "log") { - const args = await Promise.all( - msg.args().map((arg) => arg.jsonValue()), - ); - logMessages.push(JSON.stringify(args)); - } + const waitForReadyMessage = new Promise((resolve) => { + embedPage.on("console", async (msg) => { + if (msg.type() === "log") { + const args = await Promise.all(msg.args().map((arg) => arg.jsonValue())); + const logMessage = JSON.stringify(args); + logMessages.push(logMessage); + + if (logMessage.includes(`{"method":"ready"}`)) { + resolve(); // ✅ Resolve promise when "ready" appears + } + } + }); }); + await waitForReadyMessage; + const frame = embedPage.frameLocator("iframe"); await embedPage.evaluate(() => { @@ -136,14 +136,10 @@ test.describe("Embeds", () => { } }); - await embedPage.waitForTimeout(500); - await expect(frame.getByLabel("Timezone selector")).toHaveText("UTC"); - await expect( - frame.getByRole("row", { name: "Instacart $107.3k" }), - ).toBeVisible(); - expect( - logMessages.some((msg) => msg.includes(`{"id":1337,"result":true}`)), - ).toBeTruthy(); + await expect(frame.getByRole("row", { name: "Instacart $107.3k" })).toBeVisible(); + expect(logMessages.some((msg) => msg.includes(`{"id":1337,"result":true}`))).toBeTruthy(); }); + + }); diff --git a/web-admin/tests/setup/base.ts b/web-admin/tests/setup/base.ts index 889d58a1710..936bd961973 100644 --- a/web-admin/tests/setup/base.ts +++ b/web-admin/tests/setup/base.ts @@ -3,6 +3,9 @@ import { ADMIN_STORAGE_STATE, VIEWER_STORAGE_STATE } from "./constants"; import { cliLogin, cliLogout } from "./fixtures/cli"; import path from "path"; import { fileURLToPath } from "url"; +import { RILL_EMBED_SERVICE_TOKEN, RILL_ORG_NAME, RILL_PROJECT_NAME } from "./constants"; +import fs from "fs"; +import { generateEmbed } from "../utils/generate-embed"; type MyFixtures = { adminPage: Page; @@ -48,8 +51,17 @@ export const test = base.extend({ await cliLogout(); }, - embedPage: async ({ browser }, use) => { + embedPage: [async ({ browser }, use) => { const __dirname = path.dirname(fileURLToPath(import.meta.url)); + const readPath = path.join(__dirname, "..", "..", RILL_EMBED_SERVICE_TOKEN); + const rillServiceToken = fs.readFileSync(readPath, "utf-8"); + + await generateEmbed( + "bids_explore", + rillServiceToken, + RILL_ORG_NAME, + RILL_PROJECT_NAME, + ); const filePath = "file://" + path.resolve(__dirname, "..", "embed.html"); const context = await browser.newContext(); @@ -60,5 +72,5 @@ export const test = base.extend({ await use(embedPage); await context.close(); - }, + },{ scope: 'test' }], }); diff --git a/web-admin/tests/setup/setup.ts b/web-admin/tests/setup/setup.ts index 7726831c207..41e3492189e 100644 --- a/web-admin/tests/setup/setup.ts +++ b/web-admin/tests/setup/setup.ts @@ -5,7 +5,6 @@ import dotenv from "dotenv"; import path from "path"; import { fileURLToPath } from "url"; import { writeFileEnsuringDir } from "../utils/fs"; -import { generateEmbed } from "../utils/generate-embed"; import { execAsync, spawnAndMatch } from "../utils/spawn"; import type { StorageState } from "../utils/storage-state"; import { test as setup } from "./base"; @@ -135,12 +134,14 @@ setup( ); expect(orgCreateStdout).toContain("Created organization"); + // create service and write access token to file const { stdout: orgCreateService } = await execAsync( `rill service create ${RILL_SERVICE_NAME}`, ); expect(orgCreateService).toContain("Created service"); - const embedToken = orgCreateService.match(/Access token:\s+(\S+)/); + const serviceToken = orgCreateService.match(/Access token:\s+(\S+)/); + writeFileEnsuringDir(RILL_EMBED_SERVICE_TOKEN, serviceToken![1]); // Go to the organization's page await page.goto(`/${RILL_ORG_NAME}`); @@ -159,7 +160,7 @@ setup( "rill-openrtb-prog-ads", "--project", RILL_PROJECT_NAME, - "--github", + "--upload", ], /https?:\/\/[^\s]+/, ); @@ -167,9 +168,9 @@ setup( // Navigate to the GitHub auth URL // (In a fresh browser, this would typically trigger a log-in to GitHub, but we've bootstrapped the Playwright browser with GitHub auth cookies. // See the `save-github-cookies` project in `playwright.config.ts` for details.) - const url = match[0]; - await page.goto(url); - await page.waitForURL("/-/github/connect/success"); + //const url = match[0]; + //await page.goto(url); + //await page.waitForURL("/-/github/connect/success"); // Wait for the deployment to complete // TODO: Replace this with a better check. Maybe we could modify `spawnAndMatch` to match an array of regexes. @@ -203,9 +204,6 @@ setup( { intervals: Array(24).fill(5_000), timeout: 180_000 }, ) .toContain("Last refreshed"); - - // generate a embed file - writeFileEnsuringDir(RILL_EMBED_SERVICE_TOKEN, embedToken![1]); }, ); diff --git a/web-admin/tests/utils/generate-embed.ts b/web-admin/tests/utils/generate-embed.ts index 4beb94f30c5..e931434976f 100644 --- a/web-admin/tests/utils/generate-embed.ts +++ b/web-admin/tests/utils/generate-embed.ts @@ -12,7 +12,7 @@ export async function generateEmbed( ): Promise { try { const response: AxiosResponse<{ iframeSrc: string }> = await axios.post( - `http://localhost:8080/v1/${organization}/e2e/projects/${project}/iframe`, + `http://localhost:8080/v1/organizations/${organization}/projects/${project}/iframe`, { resource: resourceId, navigation: true, @@ -49,9 +49,10 @@ export async function generateEmbed( const __dirname = path.dirname(fileURLToPath(import.meta.url)); const outputPath = path.join(__dirname, "..", "embed.html"); - console.log(outputPath); + fs.writeFileSync(outputPath, htmlContent, "utf8"); } catch (error: unknown) { + console.log(error) if (error instanceof Error) { console.error("Error fetching iframe or saving file:", error.message); } else { From 23cb12e77405e98ca9a73cf2edfcb43c5f025826 Mon Sep 17 00:00:00 2001 From: Alexander Thor Date: Wed, 19 Feb 2025 21:18:09 +0100 Subject: [PATCH 25/37] fix: comments --- .../src/features/embeds/EmbedPublicAPI.svelte | 97 -------------- .../src/features/embeds/ExploreEmbed.svelte | 3 - .../features/embeds/init-embed-publicapi.ts | 118 ++++++++++++++++++ web-admin/tests/embed.html | 2 +- web-admin/tests/embeds.spec.ts | 5 +- web-admin/tests/setup/base.ts | 1 - web-admin/tests/utils/generate-embed.ts | 1 - .../dashboards/workspace/Dashboard.svelte | 26 ++++ web-common/vite.config.ts | 4 + 9 files changed, 151 insertions(+), 106 deletions(-) delete mode 100644 web-admin/src/features/embeds/EmbedPublicAPI.svelte create mode 100644 web-admin/src/features/embeds/init-embed-publicapi.ts diff --git a/web-admin/src/features/embeds/EmbedPublicAPI.svelte b/web-admin/src/features/embeds/EmbedPublicAPI.svelte deleted file mode 100644 index 90346224100..00000000000 --- a/web-admin/src/features/embeds/EmbedPublicAPI.svelte +++ /dev/null @@ -1,97 +0,0 @@ - - - diff --git a/web-admin/src/features/embeds/ExploreEmbed.svelte b/web-admin/src/features/embeds/ExploreEmbed.svelte index b78d2bb5d06..7f4b77a5f31 100644 --- a/web-admin/src/features/embeds/ExploreEmbed.svelte +++ b/web-admin/src/features/embeds/ExploreEmbed.svelte @@ -2,7 +2,6 @@ import { Dashboard } from "@rilldata/web-common/features/dashboards"; import DashboardThemeProvider from "@rilldata/web-common/features/dashboards/DashboardThemeProvider.svelte"; import StateManagersProvider from "@rilldata/web-common/features/dashboards/state-managers/StateManagersProvider.svelte"; - import EmbedPublicAPI from "@rilldata/web-admin/features/embeds/EmbedPublicAPI.svelte"; import DashboardURLStateSyncWrapper from "@rilldata/web-common/features/dashboards/url-state/DashboardURLStateSyncWrapper.svelte"; import { createRuntimeServiceGetExplore } from "@rilldata/web-common/runtime-client"; import { errorStore } from "../../components/errors/error-store"; @@ -41,9 +40,7 @@ - - diff --git a/web-admin/src/features/embeds/init-embed-publicapi.ts b/web-admin/src/features/embeds/init-embed-publicapi.ts new file mode 100644 index 00000000000..d7a8be0ff6a --- /dev/null +++ b/web-admin/src/features/embeds/init-embed-publicapi.ts @@ -0,0 +1,118 @@ +import { goto } from "$app/navigation"; +import { page } from "$app/stores"; +import { get, derived, type Readable } from "svelte/store"; + +import { getStateManagers } from "@rilldata/web-common/features/dashboards/state-managers/state-managers"; +import { convertExploreStateToURLSearchParams } from "@rilldata/web-common/features/dashboards/url-state/convertExploreStateToURLSearchParams"; +import { getDefaultExplorePreset } from "@rilldata/web-common/features/dashboards/url-state/getDefaultExplorePreset"; +import { useMetricsViewTimeRange } from "@rilldata/web-common/features/dashboards/selectors"; + +import { + getTimeControlState, + type TimeControlState, +} from "@rilldata/web-common/features/dashboards/time-controls/time-control-store"; +import { + registerRPCMethod, + emitNotification, +} from "@rilldata/web-common/lib/rpc"; + +export default function initEmbedPublicAPI(instanceId: string): () => void { + const { + metricsViewName, + validSpecStore, + dashboardStore, + timeRangeSummaryStore, + } = getStateManagers(); + + const metricsViewNameValue = get(metricsViewName); + const metricsViewTimeRange = useMetricsViewTimeRange(instanceId, metricsViewNameValue); + + const derivedState: Readable = derived( + [validSpecStore, dashboardStore, timeRangeSummaryStore, metricsViewTimeRange], + ([ + $validSpecStore, + $dashboardStore, + $timeRangeSummaryStore, + $metricsViewTimeRange, + ]) => { + const exploreSpec = $validSpecStore.data?.explore ?? {}; + const metricsViewSpec = $validSpecStore.data?.metricsView ?? {}; + + const defaultExplorePreset = getDefaultExplorePreset( + exploreSpec, + $metricsViewTimeRange?.data + ); + + let timeControlsState: TimeControlState | undefined = undefined; + if (metricsViewSpec && exploreSpec && $dashboardStore) { + timeControlsState = getTimeControlState( + metricsViewSpec, + exploreSpec, + $timeRangeSummaryStore.data?.timeRangeSummary, + $dashboardStore + ); + } + + return decodeURIComponent( + convertExploreStateToURLSearchParams( + $dashboardStore, + exploreSpec, + timeControlsState, + defaultExplorePreset + ) + ); + } + ); + + const unsubscribe = derivedState.subscribe((stateString) => { + emitNotification("stateChange", { state: stateString }); + }); + + registerRPCMethod("getState", () => { + const validSpec = get(validSpecStore); + const dashboard = get(dashboardStore); + const timeSummary = get(timeRangeSummaryStore).data; + const metricsTime = get(metricsViewTimeRange); + + const exploreSpec = validSpec.data?.explore ?? {}; + const metricsViewSpec = validSpec.data?.metricsView ?? {}; + + const defaultExplorePreset = getDefaultExplorePreset( + exploreSpec, + metricsTime?.data + ); + + let timeControlsState: TimeControlState | undefined = undefined; + if (metricsViewSpec && exploreSpec && dashboard) { + timeControlsState = getTimeControlState( + metricsViewSpec, + exploreSpec, + timeSummary?.timeRangeSummary, + dashboard + ); + } + const stateString = decodeURIComponent( + convertExploreStateToURLSearchParams( + dashboard, + exploreSpec, + timeControlsState, + defaultExplorePreset + ) + ); + return { state: stateString }; + }); + + registerRPCMethod("setState", (state: string) => { + if (typeof state !== "string") { + return new Error("Expected state to be a string"); + } + const currentUrl = new URL(get(page).url); + currentUrl.search = state; + void goto(currentUrl, { replaceState: true }); + return true; + }); + + emitNotification("ready"); + + return unsubscribe; +} \ No newline at end of file diff --git a/web-admin/tests/embed.html b/web-admin/tests/embed.html index 35ce14648de..74051460445 100644 --- a/web-admin/tests/embed.html +++ b/web-admin/tests/embed.html @@ -6,7 +6,7 @@ Iframe Example - +
Date: Wed, 19 Feb 2025 21:21:40 +0100 Subject: [PATCH 26/37] fix: format --- .../src/features/embeds/ExploreEmbed.svelte | 2 +- .../features/embeds/init-embed-publicapi.ts | 210 +++++++++--------- web-admin/tests/embeds.spec.ts | 24 +- web-admin/tests/setup/base.ts | 48 ++-- 4 files changed, 156 insertions(+), 128 deletions(-) diff --git a/web-admin/src/features/embeds/ExploreEmbed.svelte b/web-admin/src/features/embeds/ExploreEmbed.svelte index 7f4b77a5f31..fbad931a1af 100644 --- a/web-admin/src/features/embeds/ExploreEmbed.svelte +++ b/web-admin/src/features/embeds/ExploreEmbed.svelte @@ -40,7 +40,7 @@ - + diff --git a/web-admin/src/features/embeds/init-embed-publicapi.ts b/web-admin/src/features/embeds/init-embed-publicapi.ts index d7a8be0ff6a..982bbf377c0 100644 --- a/web-admin/src/features/embeds/init-embed-publicapi.ts +++ b/web-admin/src/features/embeds/init-embed-publicapi.ts @@ -8,111 +8,119 @@ import { getDefaultExplorePreset } from "@rilldata/web-common/features/dashboard import { useMetricsViewTimeRange } from "@rilldata/web-common/features/dashboards/selectors"; import { - getTimeControlState, - type TimeControlState, + getTimeControlState, + type TimeControlState, } from "@rilldata/web-common/features/dashboards/time-controls/time-control-store"; import { - registerRPCMethod, - emitNotification, + registerRPCMethod, + emitNotification, } from "@rilldata/web-common/lib/rpc"; export default function initEmbedPublicAPI(instanceId: string): () => void { - const { - metricsViewName, - validSpecStore, - dashboardStore, - timeRangeSummaryStore, - } = getStateManagers(); - - const metricsViewNameValue = get(metricsViewName); - const metricsViewTimeRange = useMetricsViewTimeRange(instanceId, metricsViewNameValue); - - const derivedState: Readable = derived( - [validSpecStore, dashboardStore, timeRangeSummaryStore, metricsViewTimeRange], - ([ - $validSpecStore, - $dashboardStore, - $timeRangeSummaryStore, - $metricsViewTimeRange, - ]) => { - const exploreSpec = $validSpecStore.data?.explore ?? {}; - const metricsViewSpec = $validSpecStore.data?.metricsView ?? {}; - - const defaultExplorePreset = getDefaultExplorePreset( - exploreSpec, - $metricsViewTimeRange?.data - ); - - let timeControlsState: TimeControlState | undefined = undefined; - if (metricsViewSpec && exploreSpec && $dashboardStore) { - timeControlsState = getTimeControlState( - metricsViewSpec, - exploreSpec, - $timeRangeSummaryStore.data?.timeRangeSummary, - $dashboardStore - ); - } - - return decodeURIComponent( - convertExploreStateToURLSearchParams( - $dashboardStore, - exploreSpec, - timeControlsState, - defaultExplorePreset - ) - ); - } - ); - - const unsubscribe = derivedState.subscribe((stateString) => { - emitNotification("stateChange", { state: stateString }); - }); - - registerRPCMethod("getState", () => { - const validSpec = get(validSpecStore); - const dashboard = get(dashboardStore); - const timeSummary = get(timeRangeSummaryStore).data; - const metricsTime = get(metricsViewTimeRange); - - const exploreSpec = validSpec.data?.explore ?? {}; - const metricsViewSpec = validSpec.data?.metricsView ?? {}; - - const defaultExplorePreset = getDefaultExplorePreset( - exploreSpec, - metricsTime?.data + const { + metricsViewName, + validSpecStore, + dashboardStore, + timeRangeSummaryStore, + } = getStateManagers(); + + const metricsViewNameValue = get(metricsViewName); + const metricsViewTimeRange = useMetricsViewTimeRange( + instanceId, + metricsViewNameValue, + ); + + const derivedState: Readable = derived( + [ + validSpecStore, + dashboardStore, + timeRangeSummaryStore, + metricsViewTimeRange, + ], + ([ + $validSpecStore, + $dashboardStore, + $timeRangeSummaryStore, + $metricsViewTimeRange, + ]) => { + const exploreSpec = $validSpecStore.data?.explore ?? {}; + const metricsViewSpec = $validSpecStore.data?.metricsView ?? {}; + + const defaultExplorePreset = getDefaultExplorePreset( + exploreSpec, + $metricsViewTimeRange?.data, + ); + + let timeControlsState: TimeControlState | undefined = undefined; + if (metricsViewSpec && exploreSpec && $dashboardStore) { + timeControlsState = getTimeControlState( + metricsViewSpec, + exploreSpec, + $timeRangeSummaryStore.data?.timeRangeSummary, + $dashboardStore, ); + } + + return decodeURIComponent( + convertExploreStateToURLSearchParams( + $dashboardStore, + exploreSpec, + timeControlsState, + defaultExplorePreset, + ), + ); + }, + ); + + const unsubscribe = derivedState.subscribe((stateString) => { + emitNotification("stateChange", { state: stateString }); + }); + + registerRPCMethod("getState", () => { + const validSpec = get(validSpecStore); + const dashboard = get(dashboardStore); + const timeSummary = get(timeRangeSummaryStore).data; + const metricsTime = get(metricsViewTimeRange); + + const exploreSpec = validSpec.data?.explore ?? {}; + const metricsViewSpec = validSpec.data?.metricsView ?? {}; + + const defaultExplorePreset = getDefaultExplorePreset( + exploreSpec, + metricsTime?.data, + ); - let timeControlsState: TimeControlState | undefined = undefined; - if (metricsViewSpec && exploreSpec && dashboard) { - timeControlsState = getTimeControlState( - metricsViewSpec, - exploreSpec, - timeSummary?.timeRangeSummary, - dashboard - ); - } - const stateString = decodeURIComponent( - convertExploreStateToURLSearchParams( - dashboard, - exploreSpec, - timeControlsState, - defaultExplorePreset - ) - ); - return { state: stateString }; - }); - - registerRPCMethod("setState", (state: string) => { - if (typeof state !== "string") { - return new Error("Expected state to be a string"); - } - const currentUrl = new URL(get(page).url); - currentUrl.search = state; - void goto(currentUrl, { replaceState: true }); - return true; - }); - - emitNotification("ready"); - - return unsubscribe; -} \ No newline at end of file + let timeControlsState: TimeControlState | undefined = undefined; + if (metricsViewSpec && exploreSpec && dashboard) { + timeControlsState = getTimeControlState( + metricsViewSpec, + exploreSpec, + timeSummary?.timeRangeSummary, + dashboard, + ); + } + const stateString = decodeURIComponent( + convertExploreStateToURLSearchParams( + dashboard, + exploreSpec, + timeControlsState, + defaultExplorePreset, + ), + ); + return { state: stateString }; + }); + + registerRPCMethod("setState", (state: string) => { + if (typeof state !== "string") { + return new Error("Expected state to be a string"); + } + const currentUrl = new URL(get(page).url); + currentUrl.search = state; + void goto(currentUrl, { replaceState: true }); + return true; + }); + + emitNotification("ready"); + + return unsubscribe; +} diff --git a/web-admin/tests/embeds.spec.ts b/web-admin/tests/embeds.spec.ts index 0bd60e66585..f8f2fecba2a 100644 --- a/web-admin/tests/embeds.spec.ts +++ b/web-admin/tests/embeds.spec.ts @@ -20,7 +20,9 @@ test.describe("Embeds", () => { const waitForReadyMessage = new Promise((resolve) => { embedPage.on("console", async (msg) => { if (msg.type() === "log") { - const args = await Promise.all(msg.args().map((arg) => arg.jsonValue())); + const args = await Promise.all( + msg.args().map((arg) => arg.jsonValue()), + ); const logMessage = JSON.stringify(args); logMessages.push(logMessage); if (logMessage.includes(`{"method":"ready"}`)) { @@ -29,7 +31,7 @@ test.describe("Embeds", () => { } }); }); - + await waitForReadyMessage; const frame = embedPage.frameLocator("iframe"); @@ -55,7 +57,9 @@ test.describe("Embeds", () => { const waitForReadyMessage = new Promise((resolve) => { embedPage.on("console", async (msg) => { if (msg.type() === "log") { - const args = await Promise.all(msg.args().map((arg) => arg.jsonValue())); + const args = await Promise.all( + msg.args().map((arg) => arg.jsonValue()), + ); const logMessage = JSON.stringify(args); logMessages.push(logMessage); @@ -105,7 +109,9 @@ test.describe("Embeds", () => { const waitForReadyMessage = new Promise((resolve) => { embedPage.on("console", async (msg) => { if (msg.type() === "log") { - const args = await Promise.all(msg.args().map((arg) => arg.jsonValue())); + const args = await Promise.all( + msg.args().map((arg) => arg.jsonValue()), + ); const logMessage = JSON.stringify(args); logMessages.push(logMessage); @@ -136,9 +142,11 @@ test.describe("Embeds", () => { }); await expect(frame.getByLabel("Timezone selector")).toHaveText("UTC"); - await expect(frame.getByRole("row", { name: "Instacart $107.3k" })).toBeVisible(); - expect(logMessages.some((msg) => msg.includes(`{"id":1337,"result":true}`))).toBeTruthy(); + await expect( + frame.getByRole("row", { name: "Instacart $107.3k" }), + ).toBeVisible(); + expect( + logMessages.some((msg) => msg.includes(`{"id":1337,"result":true}`)), + ).toBeTruthy(); }); - - }); diff --git a/web-admin/tests/setup/base.ts b/web-admin/tests/setup/base.ts index 1d7073524de..7838d7961d6 100644 --- a/web-admin/tests/setup/base.ts +++ b/web-admin/tests/setup/base.ts @@ -3,7 +3,11 @@ import { ADMIN_STORAGE_STATE, VIEWER_STORAGE_STATE } from "./constants"; import { cliLogin, cliLogout } from "./fixtures/cli"; import path from "path"; import { fileURLToPath } from "url"; -import { RILL_EMBED_SERVICE_TOKEN, RILL_ORG_NAME, RILL_PROJECT_NAME } from "./constants"; +import { + RILL_EMBED_SERVICE_TOKEN, + RILL_ORG_NAME, + RILL_PROJECT_NAME, +} from "./constants"; import fs from "fs"; import { generateEmbed } from "../utils/generate-embed"; @@ -51,25 +55,33 @@ export const test = base.extend({ await cliLogout(); }, - embedPage: [async ({ browser }, use) => { - const __dirname = path.dirname(fileURLToPath(import.meta.url)); - const readPath = path.join(__dirname, "..", "..", RILL_EMBED_SERVICE_TOKEN); - const rillServiceToken = fs.readFileSync(readPath, "utf-8"); + embedPage: [ + async ({ browser }, use) => { + const __dirname = path.dirname(fileURLToPath(import.meta.url)); + const readPath = path.join( + __dirname, + "..", + "..", + RILL_EMBED_SERVICE_TOKEN, + ); + const rillServiceToken = fs.readFileSync(readPath, "utf-8"); - await generateEmbed( - "bids_explore", - rillServiceToken, - RILL_ORG_NAME, - RILL_PROJECT_NAME, - ); - const filePath = "file://" + path.resolve(__dirname, "..", "embed.html"); + await generateEmbed( + "bids_explore", + rillServiceToken, + RILL_ORG_NAME, + RILL_PROJECT_NAME, + ); + const filePath = "file://" + path.resolve(__dirname, "..", "embed.html"); - const context = await browser.newContext(); - const embedPage = await context.newPage(); - await embedPage.goto(filePath); + const context = await browser.newContext(); + const embedPage = await context.newPage(); + await embedPage.goto(filePath); - await use(embedPage); + await use(embedPage); - await context.close(); - },{ scope: 'test' }], + await context.close(); + }, + { scope: "test" }, + ], }); From 49ad48288ce52bab655c847697e6aa0bf20cec13 Mon Sep 17 00:00:00 2001 From: Alexander Thor Date: Wed, 19 Feb 2025 21:23:49 +0100 Subject: [PATCH 27/37] .gitignore --- web-admin/.gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/web-admin/.gitignore b/web-admin/.gitignore index 0df9c1f6145..fa23d103cc8 100644 --- a/web-admin/.gitignore +++ b/web-admin/.gitignore @@ -1,2 +1,3 @@ /.svelte-kit -build \ No newline at end of file +build +tests/embed.html \ No newline at end of file From f84603d2890ba212a8bb3f5d34aad2eafa0e7dfe Mon Sep 17 00:00:00 2001 From: Alexander Thor Date: Wed, 19 Feb 2025 21:26:38 +0100 Subject: [PATCH 28/37] delete test file --- web-admin/tests/embed.html | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 web-admin/tests/embed.html diff --git a/web-admin/tests/embed.html b/web-admin/tests/embed.html deleted file mode 100644 index 74051460445..00000000000 --- a/web-admin/tests/embed.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - Iframe Example - - - - - - \ No newline at end of file From 382ffcababaf6dd13eeb10360b11c1f0402e963d Mon Sep 17 00:00:00 2001 From: Alexander Thor Date: Thu, 20 Feb 2025 13:41:29 +0100 Subject: [PATCH 29/37] fix: pr comments --- ...-publicapi.ts => init-embed-public-api.ts} | 0 web-admin/src/routes/-/embed/+layout.svelte | 4 +- web-admin/tests/embeds.spec.ts | 133 +++++------------- web-admin/tests/setup/base.ts | 4 +- web-admin/tests/utils/generate-embed.ts | 6 +- .../dashboards/workspace/Dashboard.svelte | 2 +- web-common/src/lib/rpc.ts | 2 +- web-common/vite.config.ts | 4 - 8 files changed, 43 insertions(+), 112 deletions(-) rename web-admin/src/features/embeds/{init-embed-publicapi.ts => init-embed-public-api.ts} (100%) diff --git a/web-admin/src/features/embeds/init-embed-publicapi.ts b/web-admin/src/features/embeds/init-embed-public-api.ts similarity index 100% rename from web-admin/src/features/embeds/init-embed-publicapi.ts rename to web-admin/src/features/embeds/init-embed-public-api.ts diff --git a/web-admin/src/routes/-/embed/+layout.svelte b/web-admin/src/routes/-/embed/+layout.svelte index d603455d0bf..1b803890733 100644 --- a/web-admin/src/routes/-/embed/+layout.svelte +++ b/web-admin/src/routes/-/embed/+layout.svelte @@ -2,7 +2,7 @@ import { page } from "$app/stores"; import { onMount } from "svelte"; import RuntimeProvider from "@rilldata/web-common/runtime-client/RuntimeProvider.svelte"; - import { initRPC } from "@rilldata/web-common/lib/rpc"; + import { createIframeRPCHandler } from "@rilldata/web-common/lib/rpc"; const instanceId = $page.url.searchParams.get("instance_id"); const runtimeHost = $page.url.searchParams @@ -11,7 +11,7 @@ const accessToken = $page.url.searchParams.get("access_token"); onMount(() => { - initRPC(); + createIframeRPCHandler(); }); diff --git a/web-admin/tests/embeds.spec.ts b/web-admin/tests/embeds.spec.ts index f8f2fecba2a..f44ddc268cb 100644 --- a/web-admin/tests/embeds.spec.ts +++ b/web-admin/tests/embeds.spec.ts @@ -1,152 +1,87 @@ import { expect } from "@playwright/test"; import { test } from "./setup/base"; +import { type Page } from "@playwright/test"; + +async function waitForReadyMessage(embedPage: Page, logMessages: string[]) { + return new Promise((resolve) => { + embedPage.on("console", async (msg) => { + if (msg.type() === "log") { + const args = await Promise.all(msg.args().map((arg) => arg.jsonValue())); + const logMessage = JSON.stringify(args); + logMessages.push(logMessage); + if (logMessage.includes(`{"method":"ready"}`)) { + resolve(); + } + } + }); + }); +} + test.describe("Embeds", () => { test("embeds should load", async ({ embedPage }) => { const frame = embedPage.frameLocator("iframe"); - // Set the time zone to UTC + await frame.getByLabel("Timezone selector").click(); await frame.getByRole("menuitem", { name: "UTC GMT +00:00 UTC" }).click(); - // Check the Big Number - await expect( - frame.getByRole("button", { name: "Advertising Spend Overall $1.30M" }), - ).toBeVisible(); + await expect(frame.getByRole("button", { name: "Advertising Spend Overall $1.30M" })).toBeVisible(); }); test("state is emitted for embeds", async ({ embedPage }) => { const logMessages: string[] = []; - - const waitForReadyMessage = new Promise((resolve) => { - embedPage.on("console", async (msg) => { - if (msg.type() === "log") { - const args = await Promise.all( - msg.args().map((arg) => arg.jsonValue()), - ); - const logMessage = JSON.stringify(args); - logMessages.push(logMessage); - if (logMessage.includes(`{"method":"ready"}`)) { - resolve(); - } - } - }); - }); - - await waitForReadyMessage; - + await waitForReadyMessage(embedPage, logMessages); const frame = embedPage.frameLocator("iframe"); - // Set the time zone to UTC await frame.getByLabel("Timezone selector").click(); await frame.getByRole("menuitem", { name: "UTC GMT +00:00 UTC" }).click(); - // Click a dimension row await frame.getByRole("row", { name: "Instacart $107.3k" }).click(); await embedPage.waitForTimeout(500); expect( - logMessages.some((msg) => - msg.includes("tz=UTC&f=advertiser_name+IN+('Instacart')"), - ), + logMessages.some((msg) => msg.includes("tz=UTC&f=advertiser_name+IN+('Instacart')")) ).toBeTruthy(); }); test("getState returns from embed", async ({ embedPage }) => { const logMessages: string[] = []; - - const waitForReadyMessage = new Promise((resolve) => { - embedPage.on("console", async (msg) => { - if (msg.type() === "log") { - const args = await Promise.all( - msg.args().map((arg) => arg.jsonValue()), - ); - const logMessage = JSON.stringify(args); - logMessages.push(logMessage); - - if (logMessage.includes(`{"method":"ready"}`)) { - resolve(); - } - } - }); - }); - - await waitForReadyMessage; - + await waitForReadyMessage(embedPage, logMessages); const frame = embedPage.frameLocator("iframe"); - // Set the time zone to UTC await frame.getByLabel("Timezone selector").click(); await frame.getByRole("menuitem", { name: "UTC GMT +00:00 UTC" }).click(); - // Click a dimension row await frame.getByRole("row", { name: "Instacart $107.3k" }).click(); await embedPage.waitForTimeout(500); await embedPage.evaluate(() => { const iframe = document.querySelector("iframe"); - if (iframe) { - if (iframe.contentWindow) { - iframe.contentWindow.postMessage( - { id: 1337, method: "getState" }, - "*", - ); - } - } + iframe?.contentWindow?.postMessage({ id: 1337, method: "getState" }, "*"); }); + await embedPage.waitForTimeout(500); expect( - logMessages.some((msg) => - msg.includes( - `{"id":1337,"result":{"state":"tz=UTC&f=advertiser_name+IN+('Instacart')"}}`, - ), - ), + logMessages.some((msg) => msg.includes(`{"id":1337,"result":{"state":"tz=UTC&f=advertiser_name+IN+('Instacart')"}}`)) ).toBeTruthy(); }); test("setState changes embedded explore", async ({ embedPage }) => { const logMessages: string[] = []; - - const waitForReadyMessage = new Promise((resolve) => { - embedPage.on("console", async (msg) => { - if (msg.type() === "log") { - const args = await Promise.all( - msg.args().map((arg) => arg.jsonValue()), - ); - const logMessage = JSON.stringify(args); - logMessages.push(logMessage); - - if (logMessage.includes(`{"method":"ready"}`)) { - resolve(); - } - } - }); - }); - - await waitForReadyMessage; - + await waitForReadyMessage(embedPage, logMessages); const frame = embedPage.frameLocator("iframe"); await embedPage.evaluate(() => { - const iframe = - window.document.getElementsByTagName("iframe")[0].contentWindow; - if (iframe) { - iframe.postMessage( - { - id: 1337, - method: "setState", - params: "tz=UTC&f=advertiser_name+IN+('Instacart')", - }, - "*", - ); - } + const iframe = document.querySelector("iframe"); + iframe?.contentWindow?.postMessage({ + id: 1337, + method: "setState", + params: "tz=UTC&f=advertiser_name+IN+('Instacart')" + }, "*"); }); await expect(frame.getByLabel("Timezone selector")).toHaveText("UTC"); - await expect( - frame.getByRole("row", { name: "Instacart $107.3k" }), - ).toBeVisible(); - expect( - logMessages.some((msg) => msg.includes(`{"id":1337,"result":true}`)), - ).toBeTruthy(); + await expect(frame.getByRole("row", { name: "Instacart $107.3k" })).toBeVisible(); + expect(logMessages.some((msg) => msg.includes(`{"id":1337,"result":true}`))).toBeTruthy(); }); }); diff --git a/web-admin/tests/setup/base.ts b/web-admin/tests/setup/base.ts index 7838d7961d6..d76cd315735 100644 --- a/web-admin/tests/setup/base.ts +++ b/web-admin/tests/setup/base.ts @@ -67,10 +67,10 @@ export const test = base.extend({ const rillServiceToken = fs.readFileSync(readPath, "utf-8"); await generateEmbed( - "bids_explore", - rillServiceToken, RILL_ORG_NAME, RILL_PROJECT_NAME, + "bids_explore", + rillServiceToken, ); const filePath = "file://" + path.resolve(__dirname, "..", "embed.html"); diff --git a/web-admin/tests/utils/generate-embed.ts b/web-admin/tests/utils/generate-embed.ts index fbdc7331cb4..022a291a339 100644 --- a/web-admin/tests/utils/generate-embed.ts +++ b/web-admin/tests/utils/generate-embed.ts @@ -5,16 +5,16 @@ import path from "path"; import { fileURLToPath } from "url"; export async function generateEmbed( - resourceId: string, - serviceToken: string, organization: string, project: string, + resourceName: string, + serviceToken: string, ): Promise { try { const response: AxiosResponse<{ iframeSrc: string }> = await axios.post( `http://localhost:8080/v1/organizations/${organization}/projects/${project}/iframe`, { - resource: resourceId, + resource: resourceName, navigation: true, }, { diff --git a/web-common/src/features/dashboards/workspace/Dashboard.svelte b/web-common/src/features/dashboards/workspace/Dashboard.svelte index 123655a5b9d..2a397e17181 100644 --- a/web-common/src/features/dashboards/workspace/Dashboard.svelte +++ b/web-common/src/features/dashboards/workspace/Dashboard.svelte @@ -104,7 +104,7 @@ onMount(async () => { if (isEmbedded) { initEmbedPublicAPI = ( - await import("@rilldata/web-admin/features/embeds/init-embed-publicapi.ts") + await import("@rilldata/web-admin/features/embeds/init-embed-public-api.ts") ).default; } diff --git a/web-common/src/lib/rpc.ts b/web-common/src/lib/rpc.ts index 24942cff615..835d4e89663 100644 --- a/web-common/src/lib/rpc.ts +++ b/web-common/src/lib/rpc.ts @@ -76,7 +76,7 @@ function sendError( } } -export function initRPC() { +export function createIframeRPCHandler() { const handler = (event: MessageEvent) => { if (event.source && event.data) { void handleRPCMessage(event as MessageEvent); diff --git a/web-common/vite.config.ts b/web-common/vite.config.ts index 1ff11e70825..79c190031a3 100644 --- a/web-common/vite.config.ts +++ b/web-common/vite.config.ts @@ -11,10 +11,6 @@ const alias: Alias[] = [ find: "@rilldata/web-common", replacement: "/../web-common/src", }, - { - find: "@rilldata/web-admin", - replacement: "/../web-admin/src", - }, ]; if (process.env["STORYBOOK_MODE"] === "true") { From b88380383974812386e3a0537b39eadc5d0f4db3 Mon Sep 17 00:00:00 2001 From: Alexander Thor Date: Thu, 20 Feb 2025 19:35:35 +0100 Subject: [PATCH 30/37] fix: cleaner init --- .../src/features/dashboards/workspace/Dashboard.svelte | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/web-common/src/features/dashboards/workspace/Dashboard.svelte b/web-common/src/features/dashboards/workspace/Dashboard.svelte index 2a397e17181..46ea219d028 100644 --- a/web-common/src/features/dashboards/workspace/Dashboard.svelte +++ b/web-common/src/features/dashboards/workspace/Dashboard.svelte @@ -107,13 +107,10 @@ await import("@rilldata/web-admin/features/embeds/init-embed-public-api.ts") ).default; } - await tick(); - isReady = true; - }); - $: if (isReady && initEmbedPublicAPI) { + $: if (initEmbedPublicAPI) { try { initEmbedPublicAPI(instanceId); } catch (error) { From a85c4822308e28334bbb18583bc8ecf58ab37383 Mon Sep 17 00:00:00 2001 From: Alexander Thor Date: Thu, 20 Feb 2025 19:51:44 +0100 Subject: [PATCH 31/37] fix: format --- web-admin/tests/embeds.spec.ts | 39 +++++++++++++------ web-admin/tests/setup/setup.ts | 12 ++++-- .../dashboards/workspace/Dashboard.svelte | 7 ++-- 3 files changed, 41 insertions(+), 17 deletions(-) diff --git a/web-admin/tests/embeds.spec.ts b/web-admin/tests/embeds.spec.ts index f44ddc268cb..9052e97e584 100644 --- a/web-admin/tests/embeds.spec.ts +++ b/web-admin/tests/embeds.spec.ts @@ -7,7 +7,9 @@ async function waitForReadyMessage(embedPage: Page, logMessages: string[]) { return new Promise((resolve) => { embedPage.on("console", async (msg) => { if (msg.type() === "log") { - const args = await Promise.all(msg.args().map((arg) => arg.jsonValue())); + const args = await Promise.all( + msg.args().map((arg) => arg.jsonValue()), + ); const logMessage = JSON.stringify(args); logMessages.push(logMessage); if (logMessage.includes(`{"method":"ready"}`)) { @@ -25,7 +27,9 @@ test.describe("Embeds", () => { await frame.getByLabel("Timezone selector").click(); await frame.getByRole("menuitem", { name: "UTC GMT +00:00 UTC" }).click(); - await expect(frame.getByRole("button", { name: "Advertising Spend Overall $1.30M" })).toBeVisible(); + await expect( + frame.getByRole("button", { name: "Advertising Spend Overall $1.30M" }), + ).toBeVisible(); }); test("state is emitted for embeds", async ({ embedPage }) => { @@ -40,7 +44,9 @@ test.describe("Embeds", () => { await embedPage.waitForTimeout(500); expect( - logMessages.some((msg) => msg.includes("tz=UTC&f=advertiser_name+IN+('Instacart')")) + logMessages.some((msg) => + msg.includes("tz=UTC&f=advertiser_name+IN+('Instacart')"), + ), ).toBeTruthy(); }); @@ -62,7 +68,11 @@ test.describe("Embeds", () => { await embedPage.waitForTimeout(500); expect( - logMessages.some((msg) => msg.includes(`{"id":1337,"result":{"state":"tz=UTC&f=advertiser_name+IN+('Instacart')"}}`)) + logMessages.some((msg) => + msg.includes( + `{"id":1337,"result":{"state":"tz=UTC&f=advertiser_name+IN+('Instacart')"}}`, + ), + ), ).toBeTruthy(); }); @@ -73,15 +83,22 @@ test.describe("Embeds", () => { await embedPage.evaluate(() => { const iframe = document.querySelector("iframe"); - iframe?.contentWindow?.postMessage({ - id: 1337, - method: "setState", - params: "tz=UTC&f=advertiser_name+IN+('Instacart')" - }, "*"); + iframe?.contentWindow?.postMessage( + { + id: 1337, + method: "setState", + params: "tz=UTC&f=advertiser_name+IN+('Instacart')", + }, + "*", + ); }); await expect(frame.getByLabel("Timezone selector")).toHaveText("UTC"); - await expect(frame.getByRole("row", { name: "Instacart $107.3k" })).toBeVisible(); - expect(logMessages.some((msg) => msg.includes(`{"id":1337,"result":true}`))).toBeTruthy(); + await expect( + frame.getByRole("row", { name: "Instacart $107.3k" }), + ).toBeVisible(); + expect( + logMessages.some((msg) => msg.includes(`{"id":1337,"result":true}`)), + ).toBeTruthy(); }); }); diff --git a/web-admin/tests/setup/setup.ts b/web-admin/tests/setup/setup.ts index 91c97945c9d..8dbe0e75ccf 100644 --- a/web-admin/tests/setup/setup.ts +++ b/web-admin/tests/setup/setup.ts @@ -143,7 +143,9 @@ setup.describe("global setup", () => { setup("should create an organization and service", async ({ adminPage }) => { // Create an organization named "e2e" await cliLogin(adminPage); - const { stdout: orgCreateStdout } = await execAsync(`rill org create ${RILL_ORG_NAME}`); + const { stdout: orgCreateStdout } = await execAsync( + `rill org create ${RILL_ORG_NAME}`, + ); expect(orgCreateStdout).toContain("Created organization"); // create service and write access token to file @@ -157,7 +159,9 @@ setup.describe("global setup", () => { // Go to the organization's page await adminPage.goto(`/${RILL_ORG_NAME}`); - await expect(adminPage.getByRole("heading", { name: RILL_ORG_NAME })).toBeVisible(); + await expect( + adminPage.getByRole("heading", { name: RILL_ORG_NAME }), + ).toBeVisible(); }); setup("should deploy the OpenRTB project", async ({ adminPage }) => { @@ -197,7 +201,9 @@ setup.describe("global setup", () => { // Expect to see the successful deployment await adminPage.goto(`/${RILL_ORG_NAME}/${RILL_PROJECT_NAME}`); - await expect(adminPage.getByText("Your trial expires in 30 days")).toBeVisible(); // Billing banner + await expect( + adminPage.getByText("Your trial expires in 30 days"), + ).toBeVisible(); // Billing banner await expect(adminPage.getByText(RILL_ORG_NAME)).toBeVisible(); // Organization breadcrumb await expect(adminPage.getByText("Free trial")).toBeVisible(); // Billing status await expect(adminPage.getByText(RILL_PROJECT_NAME)).toBeVisible(); // Project breadcrumb diff --git a/web-common/src/features/dashboards/workspace/Dashboard.svelte b/web-common/src/features/dashboards/workspace/Dashboard.svelte index 46ea219d028..f56d74fe444 100644 --- a/web-common/src/features/dashboards/workspace/Dashboard.svelte +++ b/web-common/src/features/dashboards/workspace/Dashboard.svelte @@ -104,7 +104,9 @@ onMount(async () => { if (isEmbedded) { initEmbedPublicAPI = ( - await import("@rilldata/web-admin/features/embeds/init-embed-public-api.ts") + await import( + "@rilldata/web-admin/features/embeds/init-embed-public-api.ts" + ) ).default; } await tick(); @@ -116,8 +118,7 @@ } catch (error) { console.error("Error running initEmbedPublicAPI:", error); } - }; - + }
Date: Thu, 20 Feb 2025 20:34:00 +0100 Subject: [PATCH 32/37] fix: lint --- web-common/src/features/dashboards/workspace/Dashboard.svelte | 1 - 1 file changed, 1 deletion(-) diff --git a/web-common/src/features/dashboards/workspace/Dashboard.svelte b/web-common/src/features/dashboards/workspace/Dashboard.svelte index f56d74fe444..9f55e274ad6 100644 --- a/web-common/src/features/dashboards/workspace/Dashboard.svelte +++ b/web-common/src/features/dashboards/workspace/Dashboard.svelte @@ -98,7 +98,6 @@ let resizing = false; let initEmbedPublicAPI; - let isReady = false; // Hacky solution to ensure that the embed public API is initialized after the dashboard is fully loaded onMount(async () => { From bb0957cb773567a47a39e2beeee1f73b03677932 Mon Sep 17 00:00:00 2001 From: Alexander Thor Date: Thu, 20 Feb 2025 21:51:39 +0100 Subject: [PATCH 33/37] fix: more lint (in my pocket) --- web-common/src/features/dashboards/workspace/Dashboard.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web-common/src/features/dashboards/workspace/Dashboard.svelte b/web-common/src/features/dashboards/workspace/Dashboard.svelte index 9f55e274ad6..20486c42246 100644 --- a/web-common/src/features/dashboards/workspace/Dashboard.svelte +++ b/web-common/src/features/dashboards/workspace/Dashboard.svelte @@ -104,7 +104,7 @@ if (isEmbedded) { initEmbedPublicAPI = ( await import( - "@rilldata/web-admin/features/embeds/init-embed-public-api.ts" + "@rilldata/web-admin/features/embeds/init-embed-public-api" ) ).default; } From e158eca5a1553ce3c062ae388d3c9fc9dfe5d538 Mon Sep 17 00:00:00 2001 From: Alexander Thor Date: Fri, 21 Feb 2025 11:38:26 +0100 Subject: [PATCH 34/37] fix: wait for bids --- web-admin/tests/setup/git/repos/rill-examples | 1 + web-admin/tests/setup/setup.ts | 14 ++++++++++++++ 2 files changed, 15 insertions(+) create mode 160000 web-admin/tests/setup/git/repos/rill-examples diff --git a/web-admin/tests/setup/git/repos/rill-examples b/web-admin/tests/setup/git/repos/rill-examples new file mode 160000 index 00000000000..76544cc7f86 --- /dev/null +++ b/web-admin/tests/setup/git/repos/rill-examples @@ -0,0 +1 @@ +Subproject commit 76544cc7f8646b9bdb4eb15b2c18370efc16b261 diff --git a/web-admin/tests/setup/setup.ts b/web-admin/tests/setup/setup.ts index 8dbe0e75ccf..4c1b367630d 100644 --- a/web-admin/tests/setup/setup.ts +++ b/web-admin/tests/setup/setup.ts @@ -229,6 +229,20 @@ setup.describe("global setup", () => { { intervals: Array(24).fill(5_000), timeout: 180_000 }, ) .toContain("Last refreshed"); + + await expect + .poll( + async () => { + await adminPage.reload(); + const listing = adminPage.getByRole("link", { + name: "Programmatic Ads Bids bids_explore", + }); + return listing.textContent(); + }, + { intervals: Array(24).fill(5_000), timeout: 180_000 }, + ) + .toContain("Last refreshed"); + }); }); From f71a539b9f1aa606847cbc4769dea2c3eca888d4 Mon Sep 17 00:00:00 2001 From: Alexander Thor Date: Fri, 21 Feb 2025 11:51:37 +0100 Subject: [PATCH 35/37] fix: format --- web-admin/tests/setup/setup.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/web-admin/tests/setup/setup.ts b/web-admin/tests/setup/setup.ts index 4c1b367630d..e0ee7b37593 100644 --- a/web-admin/tests/setup/setup.ts +++ b/web-admin/tests/setup/setup.ts @@ -242,7 +242,6 @@ setup.describe("global setup", () => { { intervals: Array(24).fill(5_000), timeout: 180_000 }, ) .toContain("Last refreshed"); - }); }); From 5c170ef611a7b6574de040d801e55716abce428b Mon Sep 17 00:00:00 2001 From: Alexander Thor Date: Fri, 21 Feb 2025 14:10:56 +0100 Subject: [PATCH 36/37] fix: nits --- web-admin/tests/setup/base.ts | 4 ++-- web-admin/tests/setup/constants.ts | 2 +- web-admin/tests/setup/git/repos/rill-examples | 1 - web-admin/tests/setup/setup.ts | 4 ++-- 4 files changed, 5 insertions(+), 6 deletions(-) delete mode 160000 web-admin/tests/setup/git/repos/rill-examples diff --git a/web-admin/tests/setup/base.ts b/web-admin/tests/setup/base.ts index d76cd315735..48d0463d0ea 100644 --- a/web-admin/tests/setup/base.ts +++ b/web-admin/tests/setup/base.ts @@ -4,7 +4,7 @@ import { cliLogin, cliLogout } from "./fixtures/cli"; import path from "path"; import { fileURLToPath } from "url"; import { - RILL_EMBED_SERVICE_TOKEN, + RILL_EMBED_SERVICE_TOKEN_FILE, RILL_ORG_NAME, RILL_PROJECT_NAME, } from "./constants"; @@ -62,7 +62,7 @@ export const test = base.extend({ __dirname, "..", "..", - RILL_EMBED_SERVICE_TOKEN, + RILL_EMBED_SERVICE_TOKEN_FILE, ); const rillServiceToken = fs.readFileSync(readPath, "utf-8"); diff --git a/web-admin/tests/setup/constants.ts b/web-admin/tests/setup/constants.ts index d32f009b650..6203aea8cf7 100644 --- a/web-admin/tests/setup/constants.ts +++ b/web-admin/tests/setup/constants.ts @@ -4,7 +4,7 @@ export const VIEWER_STORAGE_STATE = "playwright/.auth/viewer.json"; export const RILL_DEVTOOL_BACKGROUND_PROCESS_PID_FILE = "playwright/rill-devtool-background-process-pid.txt"; -export const RILL_EMBED_SERVICE_TOKEN = "playwright/rill-service-token.txt"; +export const RILL_EMBED_SERVICE_TOKEN_FILE = "playwright/rill-service-token.txt"; export const RILL_ORG_NAME = "e2e"; export const RILL_PROJECT_NAME = "openrtb"; export const RILL_SERVICE_NAME = "e2e"; diff --git a/web-admin/tests/setup/git/repos/rill-examples b/web-admin/tests/setup/git/repos/rill-examples deleted file mode 160000 index 76544cc7f86..00000000000 --- a/web-admin/tests/setup/git/repos/rill-examples +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 76544cc7f8646b9bdb4eb15b2c18370efc16b261 diff --git a/web-admin/tests/setup/setup.ts b/web-admin/tests/setup/setup.ts index e0ee7b37593..ce4974e1f95 100644 --- a/web-admin/tests/setup/setup.ts +++ b/web-admin/tests/setup/setup.ts @@ -14,7 +14,7 @@ import { test as setup } from "./base"; import { ADMIN_STORAGE_STATE, RILL_DEVTOOL_BACKGROUND_PROCESS_PID_FILE, - RILL_EMBED_SERVICE_TOKEN, + RILL_EMBED_SERVICE_TOKEN_FILE, RILL_ORG_NAME, RILL_PROJECT_NAME, RILL_SERVICE_NAME, @@ -155,7 +155,7 @@ setup.describe("global setup", () => { expect(orgCreateService).toContain("Created service"); const serviceToken = orgCreateService.match(/Access token:\s+(\S+)/); - writeFileEnsuringDir(RILL_EMBED_SERVICE_TOKEN, serviceToken![1]); + writeFileEnsuringDir(RILL_EMBED_SERVICE_TOKEN_FILE, serviceToken![1]); // Go to the organization's page await adminPage.goto(`/${RILL_ORG_NAME}`); From 01d6777b47c252345e22af3f7d9ab9577149ce95 Mon Sep 17 00:00:00 2001 From: Alexander Thor Date: Fri, 21 Feb 2025 14:13:43 +0100 Subject: [PATCH 37/37] fix: format --- web-admin/tests/setup/constants.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/web-admin/tests/setup/constants.ts b/web-admin/tests/setup/constants.ts index 6203aea8cf7..b2bb425a8cf 100644 --- a/web-admin/tests/setup/constants.ts +++ b/web-admin/tests/setup/constants.ts @@ -4,7 +4,8 @@ export const VIEWER_STORAGE_STATE = "playwright/.auth/viewer.json"; export const RILL_DEVTOOL_BACKGROUND_PROCESS_PID_FILE = "playwright/rill-devtool-background-process-pid.txt"; -export const RILL_EMBED_SERVICE_TOKEN_FILE = "playwright/rill-service-token.txt"; +export const RILL_EMBED_SERVICE_TOKEN_FILE = + "playwright/rill-service-token.txt"; export const RILL_ORG_NAME = "e2e"; export const RILL_PROJECT_NAME = "openrtb"; export const RILL_SERVICE_NAME = "e2e";