Skip to content

Commit ce3ced8

Browse files
authored
refactor: main module (#2180)
Signed-off-by: Adam Setch <[email protected]>
1 parent c29fedb commit ce3ced8

File tree

5 files changed

+363
-71
lines changed

5 files changed

+363
-71
lines changed

src/main/first-run.test.ts

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import path from 'node:path';
2+
3+
// Mocks
4+
const existsSync = jest.fn();
5+
const mkdirSync = jest.fn();
6+
const writeFileSync = jest.fn();
7+
jest.mock('node:fs', () => ({
8+
__esModule: true,
9+
default: {
10+
existsSync: (...a: unknown[]) => existsSync(...a),
11+
mkdirSync: (...a: unknown[]) => mkdirSync(...a),
12+
writeFileSync: (...a: unknown[]) => writeFileSync(...a),
13+
},
14+
existsSync: (...a: unknown[]) => existsSync(...a),
15+
mkdirSync: (...a: unknown[]) => mkdirSync(...a),
16+
writeFileSync: (...a: unknown[]) => writeFileSync(...a),
17+
}));
18+
19+
const moveToApplicationsFolder = jest.fn();
20+
const isInApplicationsFolder = jest.fn(() => false);
21+
const getPath = jest.fn(() => '/User/Data');
22+
23+
const showMessageBox = jest.fn(async () => ({ response: 0 }));
24+
25+
jest.mock('electron', () => ({
26+
app: {
27+
getPath: () => getPath(),
28+
isInApplicationsFolder: () => isInApplicationsFolder(),
29+
moveToApplicationsFolder: () => moveToApplicationsFolder(),
30+
},
31+
dialog: { showMessageBox: () => showMessageBox() },
32+
}));
33+
34+
const logError = jest.fn();
35+
jest.mock('../shared/logger', () => ({
36+
logError: (...a: unknown[]) => logError(...a),
37+
}));
38+
39+
let mac = true;
40+
jest.mock('../shared/platform', () => ({ isMacOS: () => mac }));
41+
42+
import { APPLICATION } from '../shared/constants';
43+
44+
import { onFirstRunMaybe } from './first-run';
45+
46+
describe('main/first-run', () => {
47+
beforeEach(() => {
48+
jest.clearAllMocks();
49+
mac = true;
50+
});
51+
52+
function configPath() {
53+
return path.join('/User/Data', 'FirstRun', APPLICATION.FIRST_RUN_FOLDER);
54+
}
55+
56+
it('creates first-run marker when not existing and returns true', async () => {
57+
existsSync.mockReturnValueOnce(false); // marker absent
58+
existsSync.mockReturnValueOnce(false); // folder absent
59+
await onFirstRunMaybe();
60+
expect(mkdirSync).toHaveBeenCalledWith(path.dirname(configPath()));
61+
expect(writeFileSync).toHaveBeenCalledWith(configPath(), '');
62+
});
63+
64+
it('skips writing when marker exists', async () => {
65+
existsSync.mockReturnValueOnce(true); // marker present
66+
await onFirstRunMaybe();
67+
expect(writeFileSync).not.toHaveBeenCalled();
68+
expect(mkdirSync).not.toHaveBeenCalled();
69+
});
70+
71+
it('handles fs write error gracefully', async () => {
72+
existsSync.mockReturnValueOnce(false); // marker absent
73+
existsSync.mockReturnValueOnce(true); // folder exists
74+
writeFileSync.mockImplementation(() => {
75+
throw new Error('fail');
76+
});
77+
await onFirstRunMaybe();
78+
expect(logError).toHaveBeenCalledWith(
79+
'isFirstRun',
80+
'Unable to write firstRun file',
81+
expect.any(Error),
82+
);
83+
});
84+
85+
it('prompts and moves app on macOS when user accepts', async () => {
86+
existsSync.mockReturnValueOnce(false); // marker
87+
existsSync.mockReturnValueOnce(false); // folder
88+
showMessageBox.mockResolvedValueOnce({ response: 0 });
89+
await onFirstRunMaybe();
90+
expect(moveToApplicationsFolder).toHaveBeenCalled();
91+
});
92+
93+
it('does not move when user declines', async () => {
94+
existsSync.mockReturnValueOnce(false);
95+
existsSync.mockReturnValueOnce(false);
96+
showMessageBox.mockResolvedValueOnce({ response: 1 });
97+
await onFirstRunMaybe();
98+
expect(moveToApplicationsFolder).not.toHaveBeenCalled();
99+
});
100+
101+
it('skips prompt on non-macOS', async () => {
102+
mac = false;
103+
existsSync.mockReturnValueOnce(false);
104+
existsSync.mockReturnValueOnce(false);
105+
await onFirstRunMaybe();
106+
expect(showMessageBox).not.toHaveBeenCalled();
107+
});
108+
});

src/main/first-run.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ export async function onFirstRunMaybe() {
1313
}
1414
}
1515

16-
// Ask user if the app should be moved to the applications folder.
16+
/**
17+
* Ask user if the app should be moved to the applications folder (masOS).
18+
*/
1719
async function promptMoveToApplicationsFolder() {
1820
if (!isMacOS()) return;
1921

@@ -37,7 +39,10 @@ const getConfigPath = () => {
3739
return path.join(userDataPath, 'FirstRun', APPLICATION.FIRST_RUN_FOLDER);
3840
};
3941

40-
// Whether or not the app is being run for the first time.
42+
/**
43+
* Determine if this is the first run of the application by checking for the existence of a specific file.
44+
* @returns true if this is the first run, false otherwise
45+
*/
4146
function isFirstRun() {
4247
const configPath = getConfigPath();
4348

0 commit comments

Comments
 (0)