Skip to content

Expand test coverage#1169

Merged
smashwilson merged 87 commits intomasterfrom
aw/expand-tests
Jun 7, 2019
Merged

Expand test coverage#1169
smashwilson merged 87 commits intomasterfrom
aw/expand-tests

Conversation

@smashwilson
Copy link
Contributor

@smashwilson smashwilson commented May 22, 2019

Let's expand the scope of our test suite to cover more of the extension's functionality. I'm starting here to provide us with some immediate benefits:

  • It'll enable myself, @kuychaco, and @vanessayuenn to make changes with a lower risk of causing regressions. We're new to the codebase and don't yet have a mature mental model of how the source is structured and interconnected, so we're likely to break unrelated functionality otherwise.
  • I'll have a chance to learn how things work and tour the code myself in a way that's more active than just reading it.
  • We'll be better equipped to evaluate and accept community contributions with confidence.
  • It'll be easier to update dependencies and enable dependabot.
  • We'll become less reliant on manual testing checklist issues.

Backfitting tests to existing code is tedious and time-consuming. My goal here isn't to get us to full coverage - mostly, I'm concerned with introducing a skeleton structure that lowers the barriers to adding tests organically as we work in the future. I'll call this PR "complete" once I have a test suite in place for a set of basis cases that cover the mocking needs of different parts of the codebase.

I'm also doing my best to minimize structural changes to the source. I'll likely need to change some things to make things more easily testable, but I'll endeavor to keep them small and scopes to keep this effort tractable and minimize risk.

Remaining

Modules that use the VSCode API. The primary concern here is being able to avoid collisions with globally-scoped resources like command IDs.

  • src/commands.ts
  • src/github/credentials.ts
  • src/github/pullRequestManager.ts

Modules that expect to use the network. Testing these effectively requires a way to succinctly mock GraphQL and REST responses.

  • src/github/githubRepository.ts
  • src/github/pullRequestOverview.ts

Modules that use git operations. These rely on a way to mock git commands performed through the git extension's exported API.

  • src/github/pullRequestGitHelper.ts

Source that defines React components loaded within a Webview.

  • preview-src/app.tsx

Integration tests that exercise the UI components contributed to VSCode, like the tree view and the status bar tiles.

  • src/view/prsTreeDataProvider.ts

Finally - general support infrastructure work:

  • Support *.gql imports in Mocha tests.
  • Document the Builder setup.
  • Deal with the extension under test attempting to register commands in the MockCommandRegistry during initialization

@msftclas
Copy link

msftclas commented May 22, 2019

CLA assistant check
All CLA requirements met.

@smashwilson smashwilson changed the title Infrastructure and setup for command tests Expand test coverage May 22, 2019
@rebornix
Copy link
Member

@smashwilson this is a really great effort!

When we introduced graphql into the project, we separate models and views by putting everything view related into src/view and others into their own components common, github.

For testing src/view, the only thing we need to mock (if we didn't leak other layers into it recently) is PullRequestModel and PullRequestManager. IMO we have unit tests for PullRequestModel and PullRequestManager and for src/view, we do integration tests directly by VS Code test runner.

Copy link
Contributor Author

@smashwilson smashwilson left a comment

Choose a reason for hiding this comment

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

Self-review to provide a little more context 😄

"files.trimTrailingWhitespace": true,
"editor.insertSpaces": false
"editor.insertSpaces": false,
"typescript.tsdk": "node_modules/typescript/lib"
Copy link
Contributor Author

Choose a reason for hiding this comment

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

It looks like this prompts VS Code to make the version of TypeScript from our own node_modules/ available as a language server in addition to the bundled one. I think this was added automatically when I toggled it in my workspace?

Copy link
Contributor

Choose a reason for hiding this comment

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

I would revert this, I don't think we need this setting to be on for everyone developing this project

package.json Outdated
"@types/graphql": "^14.0.5",
"@types/keytar": "^4.0.1",
"@types/lodash": "^4.14.106",
"@types/lodash.isequal": "4.5.5",
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'm not sure why we depend on @types/lodash but not lodash itself?

I believe @kuychaco found a place where we were requiring lodash directly even though it's a transitive dependency and promoted it to a direct dependency instead. We should be able to replace the places I used lodash.isequal with lodash once that's merged in.

package.json Outdated
"tslint-webpack-plugin": "^1.2.2",
"typescript": "^2.1.4",
"tty": "1.0.1",
"typescript": "3.4.5",
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 needed to upgrade TypeScript because 2.x wasn't able to follow some of the type inference shenanigans used by the builders.

"dom",
"esnext.asynciterable",
"es2017"
],
Copy link
Contributor Author

Choose a reason for hiding this comment

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

For consistency with the root tsconfig.json settings. This was necessary to successfully run tsc on preview-src/ outside of webpack, which I needed to do in order to run tests here.

I'm not sure why this tsconfig has a different target value than the root one, though? I feel like we should be able to have this one inherit from the other to stay consistent.

import { Event, Disposable } from 'vscode';
import { sep } from 'path';
import * as moment from 'moment';
import moment = require('moment');
Copy link
Contributor Author

Choose a reason for hiding this comment

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

It took me a while to figure out the difference between import moment from 'moment', import * as moment from 'moment', and import moment = require('moment').

I believe this one's correct here (and the other places I've updated it) because of this section from the TypeScript docs:

When exporting a module using export =, TypeScript-specific import module = require("module") must be used to import the module.


createGitHubRepository(remote: Remote, credentialStore: CredentialStore): GitHubRepository {
return new GitHubRepository(remote, credentialStore);
}
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 believe sinon has the ability to mock a constructor, but it always feels too invasive for my tastes. (What if there's another instantiation somewhere on the stack that I don't want to mock?)

}
});
}).catch(e => {
console.error(e.stack);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oops. I'll delete this one.

aChildProp: {linked: ChildBuilder},
});

describe('Builders', function () {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fun fact: almost all of the actual complexity of the Builder tests is in getting them to typecheck. 🌠

});

await addTests(mocha, testsRoot);
await addTests(mocha, path.resolve(testsRoot, '../../preview-src/dist/preview-src/test'));
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This needs to be a custom runner primarily so that we can add tests from preview-src/. It's also a convenient place to install the MockWebviewEnvironment.

if [ ! -f media/extension.js ]; then
printf "Running webpack\n"
webpack --env.development
fi
Copy link
Contributor Author

Choose a reason for hiding this comment

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

☝️ This bit will be useful when we run in CI.

Fun fact: the tests are loaded from the tsc-compiled JavaScript emitted by this script, but the development extension that's loaded into the extension test host uses media/extension.ts, so they both need to exist.

@smashwilson
Copy link
Contributor Author

Okay, merged in the changes from #1173 and got everything compiling and green again. Ready for review for reals this time 😄

@smashwilson smashwilson merged commit 62e2672 into master Jun 7, 2019
@smashwilson smashwilson deleted the aw/expand-tests branch June 7, 2019 19:32
Copy link
Contributor

@RMacfarlane RMacfarlane left a comment

Choose a reason for hiding this comment

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

Sorry, I missed the bus in reviewing this!

"files.trimTrailingWhitespace": true,
"editor.insertSpaces": false
"editor.insertSpaces": false,
"typescript.tsdk": "node_modules/typescript/lib"
Copy link
Contributor

Choose a reason for hiding this comment

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

I would revert this, I don't think we need this setting to be on for everyone developing this project

},
"dependencies": {
"@octokit/rest": "^16.21.0",
"@types/glob": "7.1.1",
Copy link
Contributor

Choose a reason for hiding this comment

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

this should be a dev dependency, right

*--------------------------------------------------------------------------------------------*/

import * as Octokit from '@octokit/rest';
import Octokit = require('@octokit/rest');
Copy link
Contributor

Choose a reason for hiding this comment

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

For octokit, the module ships with a .d.ts typings file, so I think the normal import syntax is fine

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants