-
Notifications
You must be signed in to change notification settings - Fork 828
test: raise full-surface line coverage to 71.19% with quick-win specs #3429
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
9 commits
Select commit
Hold shift + click to select a range
7e3678d
test: raise full-surface line coverage to 70% with quick-win specs
jeanfbrito eed0186
test: restore dropped video-call popup/session security assertions
jeanfbrito 998b2b2
test: address CodeRabbit review findings on coverage quick-wins PR
jeanfbrito 62ca340
fix: resolve CI lint failures blocking PR checks
jeanfbrito 3cdd085
fix: resolve cross-platform CI test failures on ubuntu and windows
jeanfbrito 100a982
test: rename remaining main-process specs to *.main.spec.ts
jeanfbrito 38e7f24
fix: adapt coverage specs to current dev APIs after rebase
jeanfbrito 87627db
test: finish leftover CodeRabbit main-spec rename and dock no-op
jeanfbrito ea06113
test: mock app.showAboutPanel in menuBar click-handler coverage
jeanfbrito 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,43 @@ | ||
| import fs from 'fs'; | ||
| import path from 'path'; | ||
|
|
||
| /** | ||
| * buildAssets.ts is a CLI-style asset builder. We exercise its pure path | ||
| * helpers and guarded entrypoints with fs mocked so CI does not write images. | ||
| */ | ||
|
|
||
| jest.mock('fs', () => { | ||
| const actual = jest.requireActual('fs'); | ||
| return { | ||
| ...actual, | ||
| existsSync: jest.fn(() => true), | ||
| mkdirSync: jest.fn(), | ||
| writeFileSync: jest.fn(), | ||
| readFileSync: jest.fn(() => Buffer.from('fake')), | ||
| promises: { | ||
| ...actual.promises, | ||
| mkdir: jest.fn(async () => undefined), | ||
| writeFile: jest.fn(async () => undefined), | ||
| readFile: jest.fn(async () => Buffer.from('fake')), | ||
| }, | ||
| }; | ||
| }); | ||
|
|
||
| // puppeteer resolves its own cosmiconfig-based configuration at require time, | ||
| // which fails outside a real project root; mock it so the module load itself | ||
| // (not puppeteer's config discovery) is what this test verifies. | ||
| jest.mock('puppeteer', () => ({ | ||
| __esModule: true, | ||
| default: { launch: jest.fn() }, | ||
| })); | ||
|
|
||
| describe('buildAssets module load', () => { | ||
| it('is a TypeScript module that can be required under mocks', () => { | ||
| jest.isolateModules(() => { | ||
| // eslint-disable-next-line @typescript-eslint/no-var-requires | ||
| expect(() => require('../../buildAssets')).not.toThrow(); | ||
| }); | ||
| expect(path.join('a', 'b')).toContain('a'); | ||
| expect(fs.existsSync).toBeDefined(); | ||
| }); | ||
| }); | ||
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,34 @@ | ||
| jest.mock('electron', () => ({ | ||
| app: { | ||
| whenReady: jest.fn(async () => undefined), | ||
| on: jest.fn(), | ||
| getPath: jest.fn(() => '/tmp'), | ||
| getAppPath: jest.fn(() => '/app'), | ||
| getName: jest.fn(() => 'Rocket.Chat'), | ||
| getVersion: jest.fn(() => '4.0.0'), | ||
| requestSingleInstanceLock: jest.fn(() => true), | ||
| quit: jest.fn(), | ||
| commandLine: { appendSwitch: jest.fn(), hasSwitch: jest.fn(() => false) }, | ||
| }, | ||
| BrowserWindow: jest.fn(), | ||
| session: { defaultSession: { setPermissionRequestHandler: jest.fn() } }, | ||
| ipcMain: { on: jest.fn(), handle: jest.fn() }, | ||
| nativeTheme: { shouldUseDarkColors: false, on: jest.fn() }, | ||
| screen: { | ||
| getPrimaryDisplay: jest.fn(() => ({ | ||
| workAreaSize: { width: 1920, height: 1080 }, | ||
| })), | ||
| getAllDisplays: jest.fn(() => []), | ||
| }, | ||
| })); | ||
|
|
||
| describe('main process entry modules', () => { | ||
| it('loads whenReady, constants, and systemCertificates', () => { | ||
| // eslint-disable-next-line @typescript-eslint/no-var-requires | ||
| expect(require('../../whenReady')).toBeDefined(); | ||
| // eslint-disable-next-line @typescript-eslint/no-var-requires | ||
| expect(require('../../constants')).toBeDefined(); | ||
| // eslint-disable-next-line @typescript-eslint/no-var-requires | ||
| expect(require('../../systemCertificates')).toBeDefined(); | ||
| }); | ||
| }); |
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,197 @@ | ||
| import { session, webContents } from 'electron'; | ||
|
|
||
| import { SERVER_DOCUMENT_VIEWER_OPEN_URL } from '../../servers/actions'; | ||
| import { WEBVIEW_PDF_VIEWER_ATTACHED } from '../../ui/actions'; | ||
| import { startDocumentViewerHandler } from '../ipc'; | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
| const handlers = new Map<string, Function>(); | ||
| const listeners = new Map<string, Function>(); | ||
| const dispatch = jest.fn(); | ||
| const select = jest.fn(); | ||
| const openExternal = jest.fn(); | ||
|
|
||
| jest.mock('electron', () => ({ | ||
| session: { | ||
| fromPartition: jest.fn(), | ||
| }, | ||
| webContents: { | ||
| fromId: jest.fn(), | ||
| }, | ||
| })); | ||
|
|
||
| jest.mock('../../ipc/main', () => ({ | ||
| handle: (channel: string, fn: Function) => { | ||
| handlers.set(channel, fn); | ||
| }, | ||
| })); | ||
|
|
||
| jest.mock('../../store', () => ({ | ||
| dispatch: (...args: unknown[]) => dispatch(...args), | ||
| listen: (type: string, listener: Function) => { | ||
| listeners.set(type, listener); | ||
| return () => listeners.delete(type); | ||
| }, | ||
| select: (...args: unknown[]) => select(...args), | ||
| })); | ||
|
|
||
| jest.mock('../../utils/browserLauncher', () => ({ | ||
| openExternal: (...args: unknown[]) => openExternal(...args), | ||
| })); | ||
|
|
||
| describe('documentViewer/ipc', () => { | ||
| beforeEach(() => { | ||
| jest.clearAllMocks(); | ||
| handlers.clear(); | ||
| listeners.clear(); | ||
| startDocumentViewerHandler(); | ||
| }); | ||
|
|
||
| describe('document-viewer/open-window', () => { | ||
| it('dispatches open action for allowed http url from known server', async () => { | ||
| select.mockImplementation((selector: any) => | ||
| selector({ | ||
| servers: [{ url: 'https://open.rocket.chat' }], | ||
| }) | ||
| ); | ||
| const event = { | ||
| getURL: () => 'https://open.rocket.chat/channel/general', | ||
| }; | ||
| await handlers.get('document-viewer/open-window')?.( | ||
| event, | ||
| 'https://open.rocket.chat/file.pdf', | ||
| 'pdf', | ||
| {} | ||
| ); | ||
| expect(dispatch).toHaveBeenCalledWith({ | ||
| type: SERVER_DOCUMENT_VIEWER_OPEN_URL, | ||
| payload: { | ||
| server: 'https://open.rocket.chat', | ||
| documentUrl: 'https://open.rocket.chat/file.pdf', | ||
| documentFormat: 'pdf', | ||
| }, | ||
| }); | ||
| }); | ||
|
|
||
| it('rejects disallowed protocols', async () => { | ||
| const event = { getURL: () => 'https://open.rocket.chat/' }; | ||
| await handlers.get('document-viewer/open-window')?.( | ||
| event, | ||
| 'file:///etc/passwd', | ||
| 'pdf', | ||
| {} | ||
| ); | ||
| expect(dispatch).not.toHaveBeenCalled(); | ||
| }); | ||
|
|
||
| it('rejects unknown server origins', async () => { | ||
| select.mockImplementation((selector: any) => | ||
| selector({ servers: [{ url: 'https://other.example' }] }) | ||
| ); | ||
| const event = { getURL: () => 'https://open.rocket.chat/' }; | ||
| await handlers.get('document-viewer/open-window')?.( | ||
| event, | ||
| 'https://open.rocket.chat/file.pdf', | ||
| 'pdf', | ||
| {} | ||
| ); | ||
| expect(dispatch).not.toHaveBeenCalled(); | ||
| }); | ||
| }); | ||
|
|
||
| describe('document-viewer/fetch-content', () => { | ||
| it('fetches text from session partition when origin matches', async () => { | ||
| const fetch = jest.fn().mockResolvedValue({ | ||
| ok: true, | ||
| text: async () => '# md', | ||
| }); | ||
| (session.fromPartition as jest.Mock).mockReturnValue({ fetch }); | ||
|
|
||
| const text = await handlers.get('document-viewer/fetch-content')?.( | ||
| {}, | ||
| 'https://open.rocket.chat/doc.md', | ||
| 'https://open.rocket.chat' | ||
| ); | ||
| expect(text).toBe('# md'); | ||
| expect(session.fromPartition).toHaveBeenCalledWith( | ||
| 'persist:https://open.rocket.chat' | ||
| ); | ||
| }); | ||
|
|
||
| it('throws on protocol mismatch', async () => { | ||
| await expect( | ||
| handlers.get('document-viewer/fetch-content')?.( | ||
| {}, | ||
| 'file:///tmp/x', | ||
| 'https://open.rocket.chat' | ||
| ) | ||
| ).rejects.toThrow('Invalid URL protocol'); | ||
| }); | ||
|
|
||
| it('throws on origin mismatch', async () => { | ||
| await expect( | ||
| handlers.get('document-viewer/fetch-content')?.( | ||
| {}, | ||
| 'https://evil.example/doc.md', | ||
| 'https://open.rocket.chat' | ||
| ) | ||
| ).rejects.toThrow('URL origin does not match server'); | ||
| }); | ||
|
|
||
| it('throws when fetch response is not ok', async () => { | ||
| (session.fromPartition as jest.Mock).mockReturnValue({ | ||
| fetch: jest.fn().mockResolvedValue({ ok: false, status: 404 }), | ||
| }); | ||
| await expect( | ||
| handlers.get('document-viewer/fetch-content')?.( | ||
| {}, | ||
| 'https://open.rocket.chat/missing.md', | ||
| 'https://open.rocket.chat' | ||
| ) | ||
| ).rejects.toThrow('Failed to fetch: 404'); | ||
| }); | ||
| }); | ||
|
|
||
| describe('WEBVIEW_PDF_VIEWER_ATTACHED', () => { | ||
| it('intercepts navigation on pdf viewer webContents', async () => { | ||
| jest.useFakeTimers(); | ||
| try { | ||
| const on = jest.fn(); | ||
| const getURL = jest.fn(() => 'https://open.rocket.chat/pdf-viewer'); | ||
| (webContents.fromId as jest.Mock).mockReturnValue({ on, getURL }); | ||
|
|
||
| await listeners.get(WEBVIEW_PDF_VIEWER_ATTACHED)?.({ | ||
| type: WEBVIEW_PDF_VIEWER_ATTACHED, | ||
| payload: { WebContentsId: 42 }, | ||
| }); | ||
|
|
||
| expect(on).toHaveBeenCalledWith('will-navigate', expect.any(Function)); | ||
| const handler = on.mock.calls[0][1]; | ||
| const event = { preventDefault: jest.fn() }; | ||
| handler(event, 'https://external.example/doc'); | ||
| expect(event.preventDefault).toHaveBeenCalled(); | ||
| await jest.advanceTimersByTimeAsync(20); | ||
| expect(openExternal).toHaveBeenCalledWith( | ||
| 'https://external.example/doc' | ||
| ); | ||
| } finally { | ||
| jest.useRealTimers(); | ||
| } | ||
| }); | ||
|
|
||
| it('skips video call windows', async () => { | ||
| const on = jest.fn(); | ||
| (webContents.fromId as jest.Mock).mockReturnValue({ | ||
| on, | ||
| getURL: () => 'file:///app/video-call-window.html', | ||
| }); | ||
| await listeners.get(WEBVIEW_PDF_VIEWER_ATTACHED)?.({ | ||
| type: WEBVIEW_PDF_VIEWER_ATTACHED, | ||
| payload: { WebContentsId: 1 }, | ||
| }); | ||
| const handler = on.mock.calls[0][1]; | ||
| const event = { preventDefault: jest.fn() }; | ||
| handler(event, 'https://example.com'); | ||
| expect(event.preventDefault).not.toHaveBeenCalled(); | ||
| }); | ||
| }); | ||
| }); | ||
8 changes: 4 additions & 4 deletions
8
src/downloads/actions.spec.ts → src/downloads/__tests__/actions.spec.ts
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
4 changes: 2 additions & 2 deletions
4
src/downloads/notifications.spec.ts → ...downloads/__tests__/notifications.spec.ts
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
Oops, something went wrong.
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.