-
Notifications
You must be signed in to change notification settings - Fork 0
test(e2e): Electron IPC coverage — floating, braindump, settings #96
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
fda1d12
docs: update CLAUDE.md QA policy and Electron dev/prod comparison
ryota-murakami b26e8df
test(e2e): add Electron E2E specs for settings IPC, floating window, …
ryota-murakami cd704aa
test(e2e): fix review findings — observable test names and hard-coded…
ryota-murakami 713a159
fix: pre-landing review — tighten window-count assertion after hide t…
ryota-murakami File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,125 @@ | ||
| /** | ||
| * End-to-end coverage for the BrainDump window IPC surface. | ||
| * | ||
| * Verifies that the renderer→IPC→main-process path wires the brainDump | ||
| * namespace correctly: window toggle/show, opacity read/write, sync mode, | ||
| * and shortcut config. Native Cocoa behaviors (always-on-top, vibrancy) are | ||
| * covered by local macOS native QA. | ||
| * | ||
| * Note: tests share one Electron app instance (beforeAll) and depend on | ||
| * execution order (toggle-open → then check visibility). This is an | ||
| * intentional tradeoff — Electron app launch is expensive per spec file. | ||
| */ | ||
|
|
||
| import { expect, test } from '@playwright/test' | ||
| import type { ElectronApplication, Page } from 'playwright' | ||
|
|
||
| import { LOAD_TIMEOUT_MS, setupElectronTest } from './_helpers/launch' | ||
|
|
||
| let electronApp: ElectronApplication | ||
| let mainWindow: Page | ||
|
|
||
| test.beforeAll(async () => { | ||
| ;({ electronApp, mainWindow } = await setupElectronTest('braindump-window')) | ||
| }) | ||
|
|
||
| test.afterAll(async () => { | ||
| await electronApp?.close() | ||
| }) | ||
|
|
||
| test('braindump opacity is within valid 0.30–1.00 bounds by default', async () => { | ||
| // Arrange + Act | ||
| const opacity = await mainWindow.evaluate(async () => { | ||
| const getFn = window.electronAPI?.brainDump?.getOpacity | ||
| if (!getFn) throw new Error('brainDump.getOpacity not in preload bridge') | ||
| return getFn() | ||
| }) | ||
|
|
||
| // Assert: main process clamps opacity to 0.30–1.00 on read | ||
| expect(opacity).toBeGreaterThanOrEqual(0.3) | ||
| expect(opacity).toBeLessThanOrEqual(1.0) | ||
| }) | ||
|
|
||
| test('braindump sync mode is readable after app start', async () => { | ||
| // Arrange + Act | ||
| const syncMode = await mainWindow.evaluate(async () => { | ||
| const getFn = window.electronAPI?.brainDump?.getSyncMode | ||
| if (!getFn) throw new Error('brainDump.getSyncMode not in preload bridge') | ||
| return getFn() | ||
| }) | ||
|
|
||
| // Assert: sync mode is a boolean (on or off) | ||
| expect(typeof syncMode).toBe('boolean') | ||
| }) | ||
|
|
||
| test('braindump keyboard shortcut setting is readable after app start', async () => { | ||
| // Arrange + Act | ||
| const shortcut = await mainWindow.evaluate(async () => { | ||
| const getFn = window.electronAPI?.brainDump?.getShortcut | ||
| if (!getFn) throw new Error('brainDump.getShortcut not in preload bridge') | ||
| return getFn() | ||
| }) | ||
|
|
||
| // Assert: shortcut is a string (empty string when no shortcut is configured) | ||
| expect(typeof shortcut).toBe('string') | ||
| }) | ||
|
|
||
| test('opening braindump creates a new browser window', async () => { | ||
| // Arrange: watch for the new window before calling toggle | ||
| const newWindowPromise = electronApp.waitForEvent('window', { | ||
| timeout: LOAD_TIMEOUT_MS, | ||
| }) | ||
|
|
||
| // Act | ||
| await mainWindow.evaluate(async () => { | ||
| const toggleFn = window.electronAPI?.brainDump?.toggle | ||
| if (!toggleFn) throw new Error('brainDump.toggle not in preload bridge') | ||
| await toggleFn() | ||
| }) | ||
|
|
||
| const braindumpWindow = await newWindowPromise | ||
|
|
||
| // Assert: a second window was opened | ||
| expect(braindumpWindow).toBeTruthy() | ||
| expect(electronApp.windows().length).toBeGreaterThanOrEqual(2) | ||
| }) | ||
|
|
||
| test('aux visibility reports the braindump as visible after opening', async () => { | ||
| // Arrange: braindump was shown by the previous test | ||
| // Act | ||
| const visibility = await mainWindow.evaluate(async () => { | ||
| const getFn = window.electronAPI?.window?.getAuxVisibility | ||
| if (!getFn) throw new Error('getAuxVisibility not in preload bridge') | ||
| return getFn() | ||
| }) | ||
|
|
||
| // Assert | ||
| expect(visibility.braindump).toBe(true) | ||
| }) | ||
|
|
||
| test('setting braindump opacity to 0.75 persists the exact value', async () => { | ||
| // Arrange: 0.75 is in the valid 0.30–1.00 range, so it must not be clamped | ||
| const targetOpacity = 0.75 | ||
|
|
||
| // Act | ||
| const appliedOpacity = await mainWindow.evaluate(async (opacity) => { | ||
| const setFn = window.electronAPI?.brainDump?.setOpacity | ||
| if (!setFn) throw new Error('brainDump.setOpacity not in preload bridge') | ||
| return setFn(opacity) | ||
| }, targetOpacity) | ||
|
|
||
| // Assert: main process returns the exact value when it is in range | ||
| expect(appliedOpacity).toBe(0.75) | ||
| }) | ||
|
|
||
| test('updating braindump sync mode persists without error', async () => { | ||
| // Arrange + Act: disable sync mode | ||
| const result = await mainWindow.evaluate(async () => { | ||
| const setFn = window.electronAPI?.brainDump?.setSyncMode | ||
| if (!setFn) throw new Error('brainDump.setSyncMode not in preload bridge') | ||
| return setFn(false) | ||
| }) | ||
|
|
||
| // Assert: IPC handler returns true on success | ||
| expect(result).toBe(true) | ||
| }) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,114 @@ | ||
| /** | ||
| * End-to-end coverage for the Floating Navigator window IPC surface. | ||
| * | ||
| * Verifies that the renderer→IPC→main-process path creates/shows/hides the | ||
| * floating window, and that getAuxVisibility reflects the current state. | ||
| * Native Cocoa chrome (always-on-top, vibrancy, space membership) is covered | ||
| * by local macOS native QA; only DOM/IPC-layer behaviors are tested here. | ||
| * | ||
| * Note: tests in this file share one Electron app instance (beforeAll) and | ||
| * depend on execution order because each window toggle changes shared state. | ||
| * This is an intentional tradeoff — Electron app launch is expensive, and | ||
| * the toggle sequence (hidden → shown → hidden) is itself the feature path. | ||
| */ | ||
|
|
||
| import { expect, test } from '@playwright/test' | ||
| import type { ElectronApplication, Page } from 'playwright' | ||
|
|
||
| import { LOAD_TIMEOUT_MS, setupElectronTest } from './_helpers/launch' | ||
|
|
||
| let electronApp: ElectronApplication | ||
| let mainWindow: Page | ||
|
|
||
| test.beforeAll(async () => { | ||
| ;({ electronApp, mainWindow } = await setupElectronTest('floating-window')) | ||
| }) | ||
|
|
||
| test.afterAll(async () => { | ||
| await electronApp?.close() | ||
| }) | ||
|
|
||
| test('auxiliary windows start hidden on a fresh app launch', async () => { | ||
| // Arrange + Act: fresh app — no toggle has been called | ||
| const visibility = await mainWindow.evaluate(async () => { | ||
| const getFn = window.electronAPI?.window?.getAuxVisibility | ||
| if (!getFn) throw new Error('getAuxVisibility not in preload bridge') | ||
| return getFn() | ||
| }) | ||
|
|
||
| // Assert: neither floating nor braindump are visible at startup | ||
| expect(visibility.floating).toBe(false) | ||
| expect(visibility.braindump).toBe(false) | ||
| }) | ||
|
|
||
| test('opening the floating navigator creates a new browser window', async () => { | ||
| // Arrange: listen for a new window before calling toggle | ||
| const newWindowPromise = electronApp.waitForEvent('window', { | ||
| timeout: LOAD_TIMEOUT_MS, | ||
| }) | ||
|
|
||
| // Act | ||
| await mainWindow.evaluate(async () => { | ||
| const toggleFn = window.electronAPI?.window?.toggleFloatingNavigator | ||
| if (!toggleFn) | ||
| throw new Error('toggleFloatingNavigator not in preload bridge') | ||
| await toggleFn() | ||
| }) | ||
|
|
||
| const floatingWindow = await newWindowPromise | ||
|
|
||
| // Assert: a second window was created | ||
| expect(floatingWindow).toBeTruthy() | ||
| expect(electronApp.windows().length).toBeGreaterThanOrEqual(2) | ||
| }) | ||
|
|
||
| test('aux visibility reports the floating navigator as visible after opening', async () => { | ||
| // Arrange: floating window was shown by the previous test (same electronApp instance) | ||
| // Act | ||
| const visibility = await mainWindow.evaluate(async () => { | ||
| const getFn = window.electronAPI?.window?.getAuxVisibility | ||
| if (!getFn) throw new Error('getAuxVisibility not in preload bridge') | ||
| return getFn() | ||
| }) | ||
|
|
||
| // Assert | ||
| expect(visibility.floating).toBe(true) | ||
| }) | ||
|
|
||
| test('showing the floating navigator when already open does not create a duplicate window', async () => { | ||
| // Arrange: floating is already visible (from previous test) | ||
| const windowsBefore = electronApp.windows().length | ||
|
|
||
| // Act — showFloatingNavigator should not create a second floating window | ||
| await mainWindow.evaluate(async () => { | ||
| const showFn = window.electronAPI?.window?.showFloatingNavigator | ||
| if (!showFn) throw new Error('showFloatingNavigator not in preload bridge') | ||
| await showFn() | ||
| }) | ||
|
|
||
| // Assert: window count did not increase | ||
| expect(electronApp.windows().length).toBe(windowsBefore) | ||
| }) | ||
|
|
||
| test('hiding the floating navigator makes aux visibility report it as hidden', async () => { | ||
| // Arrange: floating is visible | ||
| const windowCountBefore = electronApp.windows().length | ||
|
|
||
| // Act | ||
| await mainWindow.evaluate(async () => { | ||
| const hideFn = window.electronAPI?.window?.hideFloatingNavigator | ||
| if (!hideFn) throw new Error('hideFloatingNavigator not in preload bridge') | ||
| await hideFn() | ||
| }) | ||
|
|
||
| // Assert: aux visibility reports hidden | ||
| const visibility = await mainWindow.evaluate(async () => { | ||
| if (!window.electronAPI?.window?.getAuxVisibility) | ||
| throw new Error('getAuxVisibility not in preload bridge') | ||
| return window.electronAPI.window.getAuxVisibility() | ||
| }) | ||
| expect(visibility.floating).toBe(false) | ||
|
|
||
| // Window count must stay exactly the same (hidden, not destroyed or re-created) | ||
| expect(electronApp.windows().length).toBe(windowCountBefore) | ||
| }) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.