Skip to content
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

TimelineMarkers snapshot test #353

Merged
merged 3 commits into from
Jun 7, 2017

Conversation

gregtatum
Copy link
Member

This is my first attempt at a snapshot test for a component. My thoughts here are that snapshots should be a good first line of defense for catching component regressions. Snapshot tests would only be sanity checks for when things change. They should be fairly lightweight in the implementation, and easy to regenerate. As the components change over time you can see that the snapshots fail, inspect the output, re-generate the snapshot, and then move on with your life. I think these are good candidates for higher level connected components that have some complexity. This tests the integration of several components.

For more a unit-test level of individual components, I think enzyme would be a better fit. You can command the component at a more granular level, and check individual properties. I think the moment that we start trying to do things with the test, we should consider switching over to an enzyme test and be more strict with testing the desired output.

I would say at this point, let's create some enzyme tests of some components, and compare how this looks to a snapshot test. I think a good approach would be to go ahead and merge in these experiments, but with the option to strip them out, or refactor them.

@gregtatum gregtatum requested review from julienw and mstange May 24, 2017 15:13
@@ -102,12 +102,12 @@
"browserify": "^13.0.1",
"chai": "^3.5.0",
"css-loader": "^0.26.0",
"devtools-license-check": "^0.2.0",
Copy link
Member Author

Choose a reason for hiding this comment

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

This and other changes are npm install noise

@@ -25,6 +25,7 @@ export default class TimelineCanvas extends Component {
_requestedAnimationFrame: boolean;
_devicePixelRatio: 1;
_ctx: CanvasRenderingContext2D;
_canvas: ?HTMLCanvasElement;
Copy link
Member Author

Choose a reason for hiding this comment

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

I had to change the ref implementation to satisfy react-test-renderer

@codecov-io
Copy link

codecov-io commented May 24, 2017

Codecov Report

Merging #353 into master will increase coverage by 4.38%.
The diff coverage is 79.54%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #353      +/-   ##
==========================================
+ Coverage   31.64%   36.02%   +4.38%     
==========================================
  Files         124      123       -1     
  Lines        5947     5940       -7     
  Branches     1233     1231       -2     
==========================================
+ Hits         1882     2140     +258     
+ Misses       3484     3252     -232     
+ Partials      581      548      -33
Impacted Files Coverage Δ
src/content/components/TimelineViewport.js 24.74% <66.66%> (+24.74%) ⬆️
src/content/components/TimelineCanvas.js 57.95% <69.23%> (+57.95%) ⬆️
src/test/fixtures/mocks/canvas-context.js 94.73% <94.73%> (ø)
src/content/actions/app.js 0% <0%> (ø) ⬆️
src/test/fixtures/mocks/style-mock.js
src/content/actions/index.js
src/content/reducers/timeline-view.js 93.33% <0%> (+3.33%) ⬆️
src/content/profile-data.js 65.42% <0%> (+3.5%) ⬆️
src/content/process-profile.js 62.19% <0%> (+3.88%) ⬆️
... and 6 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 8035392...e58b49f. Read the comment docs.

@@ -17,7 +17,9 @@ import type {
import type { UpdateProfileSelection } from '../actions/profile-view';
import type { ProfileSelection } from '../actions/types';

const { DOM_DELTA_PAGE, DOM_DELTA_LINE } = new WheelEvent('mouse');
const { DOM_DELTA_PAGE, DOM_DELTA_LINE } = (typeof window === 'object' && window.WheelEvent)
Copy link
Member Author

Choose a reason for hiding this comment

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

Hmm... this was the easiest way to handle this for the test to run, otherwise I'll need to put this information into the component.

Copy link
Contributor

Choose a reason for hiding this comment

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

or you can add the WheelEvent component in window ? (or file a bug at https://github.com/tmpvar/jsdom ? or even better, do a PR there ;) )

Please at least file a bug there

Copy link
Member Author

Choose a reason for hiding this comment

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

The problem is that this runs anytime the document is imported, often before I get a chance to be able to inject something into the window, and I have to catch it for every single test that happens to touch this file.

Copy link
Contributor

Choose a reason for hiding this comment

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

oh I see ! so we can't add it ourselves but still jsdom should have it :)

window.devicePixelRatio = 1;
const ctx = mockCanvasContext();

/**
Copy link
Member Author

@gregtatum gregtatum May 24, 2017

Choose a reason for hiding this comment

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

createNodeMock is definitely the most brittle part of the test. Not super happy about mocking things out like this, but it lets us use refs.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think we can do better by mocking (with sinon or jest) stuff in HTMLCanvasElement. But that will do for now.

Copy link
Contributor

Choose a reason for hiding this comment

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

ah, I see, it's because of facebook/react#7740 :/

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, that's the reasoning for switching it up.

@gregtatum
Copy link
Member Author

@mstange This is starting to block other component testing, do you think you could take a look at it?

Copy link
Contributor

@mstange mstange left a comment

Choose a reason for hiding this comment

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

Looks good

ref='canvas'
ref={canvas => {
this._canvas = canvas;
}}
Copy link
Contributor

Choose a reason for hiding this comment

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

Do you know if the fact that you're passing a lambda here is going to cause unnecessary re-renders? In other places we've used this._takeCanvasRef to store the function.
Though it's just really one element, so it probably won't matter.

Copy link
Contributor

@julienw julienw Jun 6, 2017

Choose a reason for hiding this comment

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

what's the advantage of using this instead of this.refs.canvas ?
Ok I read the string refs are now deprecated.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm going to fix that before merging. We've been bitten too much in the past with similar types of issues.

});

afterEach(() => {
delete global.Image;
delete window.Image;
Copy link
Contributor

Choose a reason for hiding this comment

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

this will likely bite us later -- but please proceed, I'll have a look later.

expect(drawCalls).toMatchSnapshot();

delete window.requestAnimationFrame;
delete window.devicePixelRatio;
Copy link
Contributor

Choose a reason for hiding this comment

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

pretty sure this will bite us later :/ I'll have a look at how to make this better

import { storeWithProfile } from '../fixtures/stores';
import { getProfileWithMarkers } from '../store/fixtures/profiles';

jest.useFakeTimers();
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't really like that we call that here at the top level, I'd rather call it in beforeEach so that we can call useRealTimers in an afterEach.

That said I don't really understand why we use fake timers -- don't you need real timers to mock requestAnimationFrame ?

Copy link
Member Author

Choose a reason for hiding this comment

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

The timers are only fake for the scope of that file, and don't affect other ones. I think I'd prefer not to clutter up the test with extra beforeEach and afterEach declarations.

he requestAnimationFrame mock uses the setTimeout which is faked by jest. I added a few comments in the file explaining the timers and how they are running requestAnimationFrames to clarify.

Copy link
Contributor

Choose a reason for hiding this comment

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

The timers are only fake for the scope of that file,

Oh OK. Jest is different to mocha in this regard. Good!

about my comment about the fake timers, I missed the call to jest.runAllTimers();, so all is good :)

ref='container'>
ref={container => {
this._container = container;
}}>
Copy link
Contributor

@julienw julienw Jun 6, 2017

Choose a reason for hiding this comment

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

why don't you like using this.refs ? because this is deprecated

@gregtatum gregtatum merged commit 58e92b2 into firefox-devtools:master Jun 7, 2017
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.

4 participants