Skip to content
This repository has been archived by the owner on Dec 6, 2022. It is now read-only.

Add tests for launching/attaching to chrome #831

Merged
merged 10 commits into from
May 16, 2019
Merged

Conversation

EricCornelson
Copy link
Contributor

Adding tests for launching chrome (relies on microsoft/vscode-chrome-debug-core#438)

const DEBUGGER_LINE = 2;

server = createServer({ root: testProjectRoot });
server.listen(7890);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code is similar to line 118 and to the should stop on debugger statement in http://localhost test
Is there any way we can avoid duplicating it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed. We set this up in the setup() for this suite now


console.log('killing all instances of chrome');
// force kill chrome here, as it will be left open by the debug adapter (same behavior as v1)
const killCmd = (process.platform === 'win32') ? 'taskkill /F /IM chrome.exe /T' : 'killall chrome';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code is similar to the code in testSetup.tearDown. Would it be possible to unify it?

Is this behavior undesirable? If so, we should add a TODO to fix it after we release v2.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It can make troubleshooting easier. If you start Chrome, fail to attach, then kill Chrome, sometimes people might think that Chrome never actually started.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I definitely think we need to look at changing the behavior for V2 after we get the initial release out, because what essentially happens here is that the debugger attaches properly but expects to find an "about:blank" page, which we launch to when we launch chrome. If a user already has an instance of chrome running with the debug flag, it won't have that about:blank page, and the debugger bails, even if it might have been able to recover.

For now I'll create a "kill all instance of chrome" function that will get rid of the duplicated code.


const DATA_ROOT = testSetup.DATA_ROOT;

suite('Chrome Debug Adapter etc', function () {
suite('Chrome Debug Adapter etc', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mocha suggests using normal functions instead of arrow functions, so we can access the this pointer if needed: https://mochajs.org/#arrow-functions

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can still do this with arrow functions: https://stackoverflow.com/questions/15971167/how-to-increase-timeout-for-a-single-test-case-in-mocha. I would vote for using arrow functions here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, the reason I changed this to an arrow function was mainly just because of our lint rules that will flag any regular function declarations, and were red-lining the entire test in the editor. The other option is just to add tslint exceptions for all the tests, but that's also kind of annoying. There's a big long discussion about arrow functions vs normal here. There's been some chatter about redesigns in mocha to better support arrow functions, but so far it doesn't seem to have gone anywhere.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW, Jasmine also recommends function over ()=>{}
https://stackoverflow.com/questions/38915090/are-arrow-functions-recommended-in-jasmine-test-cases.

Personally, I'd prefer to use arrow functions almost everywhere since it makes javascript behave more like C#, Java, and others in regards to this, and of course I'm more familiar with those languages, but it seems like the JS community (at least right now) still relies pretty heavily on the current this behavior.

So I can go either way on this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use the arrow function then. If we ever need to do something which the only way to do it is to get the this pointer, then we can change that particular call to use a function instead.


server = createServer({ root: testProjectRoot });
server.listen(7890);
const browser = await puppeteer.launch(<puppeteer.LaunchOptions>{ headless: false, args: ['http://localhost:7890', '--remote-debugging-port=7777'] });
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this cast needed?

Copy link
Contributor Author

@EricCornelson EricCornelson May 7, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not needed for anything except intellisense. For whatever reason, VS Code doesn't provide intellisense on anonymous objects, even when the type of the argument is known for that function unless you provide a type assertion. We can remove it if you'd prefer, I typically just toss them on there when I need to check what the possible values can be.

**EDIT: figured out the problem on my end with the intellisense, turns out it was an extension (TypeScript Importer) causing the problems just for me.

@@ -30,7 +30,8 @@ class NotYetLaunched implements IChromeLauncherLifetimeState {
}

public stop(): Promise<void> {
throw new Error(`Can't stop the chrome process because it hasn't been launched yet`);
// no-op here because in the case that we attach to an existing instance the launcher will still be in this state
return new Promise(a => a());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Promise.resolve()


console.log('killing all instances of chrome');
// force kill chrome here, as it will be left open by the debug adapter (same behavior as v1)
const killCmd = (process.platform === 'win32') ? 'taskkill /F /IM chrome.exe /T' : 'killall chrome';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It can make troubleshooting easier. If you start Chrome, fail to attach, then kill Chrome, sometimes people might think that Chrome never actually started.

try {
await Promise.all([
dc.configurationSequence(),
dc.launch({ url: 'http://localhost:7890', timeout: 2000, webRoot: testProjectRoot }),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd specify the port here as an explicit parameter, to make it clearer that we are going to be using the same port...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

dc.launch({ url: 'http://localhost:7890', timeout: 2000, webRoot: testProjectRoot }),
]);
} catch (err) {
expect(err.message).to.satisfy( (x: string) => x.startsWith('Cannot connect to runtime process, timeout after 2000 ms'));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this test fail if dc.launch somehow succeeds?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Right now since we're just baselining V1 behavior, we are expecting this to throw an exception. Once we decide it's an appropriate time to fix this, then we can change the test accordingly. I'll add a note in the test that explains this so that it's clear what we're expecting from the test

dc.assertStoppedLocation('debugger_statement', { path: breakFile, line: DEBUGGER_LINE } )
]);

await browser.close();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be a good idea to put the .close() in a finally block, so we'll close the browser even if the test fails...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good suggestion. I added a finally block for both tests that close with puppeteer.

* Kills all running instances of chrome on the test host
*/
export function killAllChrome() {
const killCmd = (process.platform === 'win32') ? 'taskkill /F /IM chrome.exe /T' : 'killall chrome';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this kill all the chrome.exe instances on my desktop every time I run the tests?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Unless there's an easy way to get the PID of the instance that the debug adapter launched so we can just close that specific instance, I don't see another option other than just force killing all chrome instances here. Let me know if you know of a better way.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested on my PC and "wmic process where ExecutablePath='path goes here\chrome-win\chrome.exe' delete" can kill only the chrome.exe instances at a specific path

-Make debugging port explicit in tests
-Put puppeteer browser.close() in finally blocks
-Add context around the "should throw error when launching if chrome debug port is in use" test.
command.

The kill command (or taskkill) will return with non-zero codes if the
process is already dead. In which case, we don't care (we do the same
thing elsewhere)
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants