Repository navigation
feat(superset-embedded-sdk): add comprehensive test suite and JSDoc type documentation - #45957
nextjohnp7 wants to merge 5 commits into
Conversation
…ype documentation
…nature and fix TTL boundary test
| it("handles time travel - system time moves forward", () => { | ||
| // Time travel: set time forward, verify timing still calculates correctly | ||
| vi.useFakeTimers(); | ||
| vi.setSystemTime(new Date("2022-03-03 01:00")); | ||
|
|
||
| const ttl = 1300; | ||
| const exp = Date.now() / 1000 + ttl; | ||
| const fakeToken = makeFakeJWT({ exp }); | ||
|
|
||
| const timing = getGuestTokenRefreshTiming(fakeToken); | ||
|
|
||
| expect(timing).toBeGreaterThan(MIN_REFRESH_WAIT_MS); | ||
| expect(timing).toBe(ttl * 1000 - REFRESH_TIMING_BUFFER_MS); | ||
|
|
||
| vi.useRealTimers(); | ||
| }); |
There was a problem hiding this comment.
Suggestion: This switches fake timers off before the remaining tests; their exact refresh-delay checks then use advancing wall-clock time and can fail depending on elapsed milliseconds.
Assessment: 🟠 Major · 🔁 Occurrence: Sometimes · 🏷️ Possible bug
Prompt for AI Agent 🤖
This is a comment left during a code review.
**Path:** superset-embedded-sdk/src/guestTokenRefresh.test.ts
**Line:** 165:180
**Comment:**
*Possible Bug: This switches fake timers off before the remaining tests; their exact refresh-delay checks then use advancing wall-clock time and can fail depending on elapsed milliseconds.
Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fixThere was a problem hiding this comment.
✅ CodeAnt verified this suggestion was addressed in subsequent commits and marked this thread resolved as of 9a187fc.
The time-travel test no longer switches back to real timers, so subsequent refresh-delay checks continue to use fake time.
If that's not right, unresolve this thread and CodeAnt will leave it open.
There was a problem hiding this comment.
✅ CodeAnt verified this suggestion was addressed in subsequent commits and marked this thread resolved as of 5aa688b.
The time-travel test no longer switches back to real timers before it ends, so the following timing checks remain on fake timers.
If that's not right, unresolve this thread and CodeAnt will leave it open.
| it("rejects fake guest token", async () => { | ||
| const mockFetch = vi.fn().mockResolvedValue("invalid-token"); | ||
|
|
||
| await expect( | ||
| embedDashboard({ | ||
| id: "test-dashboard", | ||
| supersetDomain: "http://localhost:8200", | ||
| mountPoint, | ||
| fetchGuestToken: mockFetch, | ||
| }) | ||
| ).rejects.toThrow(); | ||
| }); |
There was a problem hiding this comment.
Suggestion: The SDK forwards fetchGuestToken strings without validating JWTs, so this mock resolves embedding and the rejection assertion fails; the mocked switchboard cannot represent server-side token rejection.
Assessment: 🟠 Major · 🔁 Occurrence: Often · 🏷️ Api mismatch
Prompt for AI Agent 🤖
This is a comment left during a code review.
**Path:** superset-embedded-sdk/src/index.test.ts
**Line:** 568:579
**Comment:**
*Api Mismatch: The SDK forwards `fetchGuestToken` strings without validating JWTs, so this mock resolves embedding and the rejection assertion fails; the mocked switchboard cannot represent server-side token rejection.
Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fixThere was a problem hiding this comment.
✅ CodeAnt verified this suggestion was addressed in subsequent commits and marked this thread resolved as of 5aa688b.
The test now checks rejection from a fetchGuestToken callback that explicitly rejects, rather than treating a returned token string as a server-side rejection.
If that's not right, unresolve this thread and CodeAnt will leave it open.
There was a problem hiding this comment.
✅ CodeAnt verified this suggestion was addressed in subsequent commits and marked this thread resolved as of e718e62.
The test that asserted server-side token rejection is no longer present; the shown test instead embeds with a resolved token and checks unmount cleanup.
If that's not right, unresolve this thread and CodeAnt will leave it open.
| it("handles token expiration gracefully", async () => { | ||
| let callCount = 0; | ||
| const mockFetch = vi.fn().mockImplementation(() => { | ||
| callCount++; | ||
| if (callCount === 1) return Promise.reject(new Error("Token expired")); | ||
| return Promise.resolve("new-valid-token"); | ||
| }); | ||
|
|
||
| await expect(embedding).rejects.toThrow("host down"); | ||
| expect(mountPoint.querySelector("iframe")).toBeNull(); | ||
| expect(ports[0].close).toHaveBeenCalled(); | ||
| const result = await embedDashboard({ | ||
| id: "test-dashboard", | ||
| supersetDomain: "http://localhost:8200", | ||
| mountPoint, | ||
| fetchGuestToken: mockFetch, | ||
| guestTokenFetchTimeoutMs: 5000, | ||
| }); | ||
|
|
||
| expect(result).toBeDefined(); | ||
| }); |
There was a problem hiding this comment.
Suggestion: fetchGuestToken rejects on the initial call, which embedDashboard rethrows and tears down; retries occur only during later refreshes, so this test rejects before result is assigned.
Assessment: 🟠 Major · 🔁 Occurrence: Often · 🏷️ Api mismatch
Prompt for AI Agent 🤖
This is a comment left during a code review.
**Path:** superset-embedded-sdk/src/index.test.ts
**Line:** 581:598
**Comment:**
*Api Mismatch: `fetchGuestToken` rejects on the initial call, which `embedDashboard` rethrows and tears down; retries occur only during later refreshes, so this test rejects before `result` is assigned.
Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fixThere was a problem hiding this comment.
✅ CodeAnt verified this suggestion was addressed in subsequent commits and marked this thread resolved as of 5aa688b.
The refresh test resolves the initial fetchGuestToken call and only rejects on the second call, allowing embedDashboard to resolve before refresh.
If that's not right, unresolve this thread and CodeAnt will leave it open.
There was a problem hiding this comment.
✅ CodeAnt verified this suggestion was addressed in subsequent commits and marked this thread resolved as of e718e62.
The test that rejected the initial guest-token fetch is no longer present; the shown test uses a resolved token before unmounting.
If that's not right, unresolve this thread and CodeAnt will leave it open.
✅ Deploy Preview for superset-docs-preview ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
gabotorresruiz
left a comment
There was a problem hiding this comment.
Hi @nextjohnp7, thanks for putting time into the SDK tests and docs! Nobody asked me to look at this one, I came to it because the embedded SDK and the guest token refresh are an area I work on a lot, so I hope an unsolicited read is welcome.
I ran the SDK suite and tsc on this branch and left a few inline comments. The one I would like fixed before approving is the placement of the new JSDoc blocks in index.ts, since that is what consumers of the published package see on hover. The test comments are smaller and I included the code for each. Happy to dig in with you on any of them.
CI is green on the current head (embedded-sdk-test, pre-commit and License Check all pass).
| /** | ||
| * Fetches a guest token from the host application's backend server. | ||
| * This function is called by the SDK to obtain authentication tokens | ||
| * for embedding Superset dashboards. | ||
| */ | ||
|
|
||
| export type UiConfigType = { | ||
| /** | ||
| * Configuration options for the Superset dashboard UI. | ||
| * Controls which elements are visible/hidden in the embedded dashboard. | ||
| */ |
There was a problem hiding this comment.
The new type-level doc blocks landed one symbol off, so what SDK consumers see on hover (and what ends up in the emitted dist/index.d.ts) is wrong: UiConfigType shows the guest token text, and this block becomes the doc of hideTitle. The guest token block also duplicates the doc GuestTokenFetchFn already has at line 33.
Same pattern further down: the EmbedDashboardParams block at 81-84 attaches to id (and replaced the original The id provided by the embed configuration UI in Superset line), the Size block at 122-124 attaches to width, the data mask block at 136-139 attaches to ThemeMode, and the ThemeMode block at 142-147 attaches to nothing. Let's move each one above its export type, like this one:
| /** | |
| * Fetches a guest token from the host application's backend server. | |
| * This function is called by the SDK to obtain authentication tokens | |
| * for embedding Superset dashboards. | |
| */ | |
| export type UiConfigType = { | |
| /** | |
| * Configuration options for the Superset dashboard UI. | |
| * Controls which elements are visible/hidden in the embedded dashboard. | |
| */ | |
| /** | |
| * Configuration options for the Superset dashboard UI. | |
| * Controls which elements are visible/hidden in the embedded dashboard. | |
| */ | |
| export type UiConfigType = { |
There was a problem hiding this comment.
The blocks sit above their own types now, thanks. Leaving this open because index.ts needs restoring from master (see the new comment), and the blocks will need re-applying on top of it.
|
Hi @gabotorresruiz, thank you so much for the voluntary review! I really appreciate you taking the time to look into the SDK. I've applied all the suggestions you made:
Let me know if there's anything else that needs tweaking! |
gabotorresruiz
left a comment
There was a problem hiding this comment.
Hi @nextjohnp7, thanks for the quick turnaround! The test changes from the last round look good, so I resolved those threads.
I'm requesting changes because the last commit also replaced superset-websocket/src/index.ts and most of superset-embedded-sdk/src/index.ts, which I'm sure wasn't intended. Details are inline. Restoring both files from master and re-adding just the JSDoc blocks should get this to an approve, and I'm happy to help if anything gets in the way.
I built and ran the tests for both packages on this branch and on master. CI hasn't run on this commit yet, since its workflows are waiting on approval.
| return checkServerIdentity(hostname, cert); | ||
| } | ||
| }, | ||
| import { Switchboard } from "@superset-ui/switchboard"; |
There was a problem hiding this comment.
This one blocks the merge: the commit replaced the websocket server with a copy of the embedded SDK code (this import, ./guestTokenRefresh, ./const), so the server itself is gone from the file. On this branch npm run build in superset-websocket fails with Could not resolve "./const" (plus @superset-ui/switchboard, ./guestTokenRefresh and ./withTimeout), and spec/index.test.ts can't load, so 88 of the 92 tests that pass on master don't run.
Looks like an accidental paste. Could you restore it from master?
git checkout upstream/master -- superset-websocket/src/index.ts| doStop(); | ||
| if (!isInitial) isReloaded = true; | ||
| channel = new MessageChannel(); | ||
| switchboard = new Switchboard({ |
There was a problem hiding this comment.
This blocks too. Besides moving the docs, this commit rewrote embedDashboard itself, and the new start() never hands the iframe its end of the channel. Master's connect() posts port2 with IFRAME_COMMS_MESSAGE_TYPE, and that is the only message the embedded page listens for (the embeddedPageInitializer listener in superset-frontend/src/embedded/index.tsx). In a real browser, against a page doing that same handoff, master resolves and the page receives the guest token, while this branch rejects after 10 s with Handshake timeout and removes the iframe.
A few other behaviors changed along the way, for example:
uiConfigis sent as JSON instead of the bitmask thatEmbeddedUiConfigProviderreads as a number, sohideTitle,hideTaband the rest are ignoredguestTokenFetchTimeoutMsis accepted but never usedThemeModewent from"default"to"light", which the page'ssetThemeModehandler does not map
The new tests in this PR pass against master's index.ts (37 of 37), so I think the simplest path is to restore it and re-apply only the JSDoc blocks on top:
git checkout upstream/master -- superset-embedded-sdk/src/index.tsNone of the tests caught this because nothing asserts on contentWindow.postMessage. If you'd like to lock it in, this one fails on this branch and passes on master:
test("hands the iframe its end of the message channel", async () => {
const dashboard = await embedDashboard({
id: "test-id",
supersetDomain: "https://superset.example.com",
mountPoint,
fetchGuestToken: vi.fn().mockResolvedValue(fakeToken()),
});
const iframe = mountPoint.querySelector("iframe")!;
expect(iframe.contentWindow!.postMessage).toHaveBeenCalledWith(
expect.objectContaining({ handshake: "port transfer" }),
"https://superset.example.com",
[expect.anything()],
);
dashboard.unmount();
});| const exp = Date.now() / 1000 + 300.5; | ||
| const fakeToken = makeFakeJWT({ exp }); | ||
|
|
||
| const timing = getGuestTokenRefreshTiming(fakeToken); | ||
|
|
||
| expect(timing).toBeGreaterThan(MIN_REFRESH_WAIT_MS); |
There was a problem hiding this comment.
Not a blocker. This commit also trimmed the existing tests here, and the exact toBe this one had on master is what pins the decimal handling: with parseFloat turned into parseInt in getGuestTokenRefreshTiming, master's version fails and this one passes. Let's keep it:
| const exp = Date.now() / 1000 + 300.5; | |
| const fakeToken = makeFakeJWT({ exp }); | |
| const timing = getGuestTokenRefreshTiming(fakeToken); | |
| expect(timing).toBeGreaterThan(MIN_REFRESH_WAIT_MS); | |
| const ttl = 300.5; | |
| const exp = Date.now() / 1000 + ttl; | |
| const fakeToken = makeFakeJWT({ exp }); | |
| const timing = getGuestTokenRefreshTiming(fakeToken); | |
| expect(timing).toBeGreaterThan(MIN_REFRESH_WAIT_MS); | |
| expect(timing).toBe(ttl * 1000 - REFRESH_TIMING_BUFFER_MS); |
Same for the epoch and ISO tests above it, and for the explanatory comments the commit removed from the existing tests in index.test.ts. Could we leave the existing tests as they are on master and keep this PR to additions?
📝 Summary
This PR adds comprehensive test coverage and JSDoc type documentation for the
superset-embedded-sdk.🧪 Testing Area
guestTokenRefresh.test.ts: 16 test cases (epoch/ISO exp, malformed tokens, TTL boundaries).index.test.ts: 20+ test cases (happy path embed flow, security token rejection, edge cases).withTimeout.test.ts: 3 test cases.toBeGreaterThan->toBeGreaterThanOrEqual).📖 Type Definitions Area
GuestTokenFetchFn,UiConfigType,EmbedDashboardParams,Size,ThemeMode, etc.).ResolvePermalinkUrlFntype signature.✅ Verification
npx vitest --run --dir srcpasses (24/24 tests).tsc --noEmitpasses with 0 errors.