Skip to content

Commit 8be5201

Browse files
saintsebastiankumar303
authored andcommitted
fix: The web-ext run process can be suspended again (#948)
1 parent cfdd21d commit 8be5201

File tree

2 files changed

+62
-3
lines changed

2 files changed

+62
-3
lines changed

src/cmd/run.js

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ export type ReloadStrategyOptions = {|
113113
createWatcher?: WatcherCreatorFn,
114114
createFileFilter?: FileFilterCreatorFn,
115115
stdin: stream$Readable,
116+
kill: typeof process.kill;
116117
|};
117118

118119
export function defaultReloadStrategy(
@@ -124,6 +125,7 @@ export function defaultReloadStrategy(
124125
addonReload = defaultAddonReload,
125126
createWatcher = defaultWatcherCreator,
126127
stdin = process.stdin,
128+
kill = process.kill,
127129
}: ReloadStrategyOptions = {}
128130
): void {
129131
const watcher: Watchpack = createWatcher({
@@ -143,8 +145,11 @@ export function defaultReloadStrategy(
143145
// NOTE: this `Promise.resolve().then(...)` is basically used to spawn a "co-routine" that is executed
144146
// before the callback attached to the Promise returned by this function (and it allows the `run` function
145147
// to not be stuck in the while loop).
148+
149+
const keypressUsageInfo = 'Press R to reload (and Ctrl-C to quit)';
150+
146151
Promise.resolve().then(async function() {
147-
log.info('Press R to reload (and Ctrl-C to quit)');
152+
log.info(keypressUsageInfo);
148153

149154
let userExit = false;
150155

@@ -155,6 +160,25 @@ export function defaultReloadStrategy(
155160

156161
if (keyPressed.ctrl && keyPressed.name === 'c') {
157162
userExit = true;
163+
} else if (keyPressed.ctrl && keyPressed.name === 'z') {
164+
// Prepare to suspend.
165+
166+
// NOTE: Switch the raw mode off before suspending (needed to make the keypress event to work correctly
167+
// when the nodejs process is resumed).
168+
if (stdin instanceof tty.ReadStream) {
169+
stdin.setRawMode(false);
170+
}
171+
172+
log.info('\nweb-ext has been suspended on user request');
173+
kill(process.pid, 'SIGTSTP');
174+
175+
// Prepare to resume.
176+
177+
log.info(`\nweb-ext has been resumed. ${keypressUsageInfo}`);
178+
// Switch the raw mode on on resume.
179+
if (stdin instanceof tty.ReadStream) {
180+
stdin.setRawMode(true);
181+
}
158182
} else if (keyPressed.name === 'r' && addonId) {
159183
log.debug('Reloading extension on user request');
160184
await addonReload({addonId, client});

tests/unit/test-cmd/test.run.js

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,17 @@ describe('run', () => {
385385
kill = sinon.spy(() => {});
386386
}
387387

388+
function loopExiter(stdin) {
389+
return () => stdin.emit('keypress', 'c', {name: 'c', ctrl: true});
390+
}
391+
392+
function exitLoopOnError(stdin) {
393+
return (error) => {
394+
loopExiter(stdin);
395+
throw error;
396+
};
397+
}
398+
388399
function prepare() {
389400
const client = new RemoteFirefox(fakeFirefoxClient());
390401
const watcher = {
@@ -403,6 +414,7 @@ describe('run', () => {
403414
addonReload: sinon.spy(() => Promise.resolve()),
404415
createWatcher: sinon.spy(() => watcher),
405416
stdin: new stream.Readable(),
417+
kill: sinon.spy(() => Promise.resolve()),
406418
};
407419
return {
408420
...args,
@@ -462,19 +474,42 @@ describe('run', () => {
462474
assert.ok(addonReload.called);
463475
assert.equal(addonReload.firstCall.args[0].addonId,
464476
tempInstallResult.addon.id);
465-
});
477+
})
478+
.then(loopExiter(fakeStdin), exitLoopOnError(fakeStdin));
466479
});
467480

468481
it('shuts down firefox on user request (CTRL+C in shell console)', () => {
469482
const {firefoxProcess, reloadStrategy} = prepare();
470483
const fakeStdin = new tty.ReadStream();
484+
sinon.spy(fakeStdin, 'setRawMode');
471485

472486
return reloadStrategy({}, {stdin: fakeStdin})
473487
.then(() => {
474488
fakeStdin.emit('keypress', 'c', {name: 'c', ctrl: true});
475489
}).then(() => {
476490
assert.ok(firefoxProcess.kill.called);
477-
});
491+
})
492+
.then(loopExiter(fakeStdin), exitLoopOnError(fakeStdin));
493+
});
494+
495+
it('pauses the web-ext process (CTRL+Z in shell console)', () => {
496+
const {reloadStrategy, kill} = prepare();
497+
const fakeStdin = new tty.ReadStream();
498+
const setRawMode = sinon.spy(fakeStdin, 'setRawMode');
499+
500+
return reloadStrategy({}, {stdin: fakeStdin})
501+
.then(() => {
502+
fakeStdin.emit('keypress', 'z', {name: 'z', ctrl: true});
503+
}).then(() => {
504+
assert.ok(kill.called);
505+
assert.deepEqual(kill.firstCall.args, [process.pid, 'SIGTSTP']);
506+
sinon.assert.callOrder(setRawMode, setRawMode, kill, setRawMode);
507+
sinon.assert.calledThrice(setRawMode);
508+
assert.equal(setRawMode.firstCall.args[0], true);
509+
assert.equal(setRawMode.secondCall.args[0], false);
510+
assert.equal(setRawMode.lastCall.args[0], true);
511+
})
512+
.then(loopExiter(fakeStdin), exitLoopOnError(fakeStdin));
478513
});
479514

480515
});

0 commit comments

Comments
 (0)