Skip to content
This repository was archived by the owner on Jan 11, 2023. It is now read-only.

Test report#2780

Merged
jasonLaster merged 1 commit into
firefox-devtools:masterfrom
jasonLaster:cov-report
Jun 14, 2017
Merged

Test report#2780
jasonLaster merged 1 commit into
firefox-devtools:masterfrom
jasonLaster:cov-report

Conversation

@jasonLaster

@jasonLaster jasonLaster commented May 2, 2017

Copy link
Copy Markdown
Contributor

Summary of Changes

This starts a blog post on the state of our testing and type coverage. I hope to write one quarterly to summarize our progress and next steps.

This is helping me better understand what some good next steps are:

  1. adding State types in our actions
  2. adding types to our tests
  3. adding types to our component props
  4. adding immutable types

Comment thread src/reducers/types.js
export type State = {
pause: PauseState,
sources: SourcesState,
breakpoints: BreakpointsState

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I should add the remaining reducers

Comment thread src/test/integration/runner.js Outdated
it("asm", async () => await asm(ctx));

it("breakpoints - toggle", async () => await breakpoints.toggle(ctx));
it.only("breakpoints - toggle", async () => await breakpoints.toggle(ctx));

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

oops

Comment thread src/utils/frame.js Outdated
}

export function formatDisplayName(frame: Frame) {
export function formatDisplayName(frame: LocalFrame): string {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I should make sure this is imported

Comment thread src/utils/parser/utils.js Outdated
const isEmpty = require("lodash/isEmpty");
const uniq = require("lodash/uniq");

import type { Node, Path, Program } from "babylon";

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

adding babylon types

Comment thread src/utils/prefs.js Outdated
pendingSelectedLocation: Object,
pendingBreakpoints: any[],
expressions: Object
};

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

adding pref types is really helpful

Comment thread docs/updates/5-1-2017-flow.md Outdated

There have been a couple places where flow types have been difficult to get right.

1. **contravariants** `this.toggleFramesDisplay = this.toggleFramesDisplay.bind(this)`

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

contravariance?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

yeah - i think that's right :)

@thejameskyle did i get it right?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Class methods are covariant in Flow by default, which in effect means they are "read-only" and cannot be assigned to.

class Error extends React.Component {
  constructor(props) {
    super(props);
    this.onClick = this.onClick.bind(this); // Error
    //   ^ property `onClick`. Covariant property `onClick` incompatible with contravariant use in
    //     assignment of property `onClick`
  }
  
  onClick(event: ButtonClickEvent) {
    // ...
  }
  
  render() {
    return <button onClick={this.onClick}>Click Me</button>;
  }
}

In order to make this work, you need to use an additional class field, which are invariant (read & write) by default, like this:

class WorksWithClassPropertyType extends React.Component {
  constructor(props) {
    super(props);
    this.onClick = this.onClick.bind(this); // Works!
  }
  
  onClick: (ButtonClickEvent) => void;
  onClick(event) {
    // ...
  }
}

Alternatively you can use the class properties proposal to get around all of this:

class WorksWithClassPropertyArrowFunction extends React.Component {
  onClick = (event: ButtonClickEvent) => {
    // ...
  };
}

[See in Try Flow]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@thejameskyle Great explanation, was wondering about why this happens myself too.

Is it the same case when I try to set custom toString() on some function?

const createActionCreator = (type, actionCreator) = {
  actionCreator.toString = () => type // error!
  return actionCreator
}

is there any workaround for this?

Comment thread docs/updates/5-1-2017-flow.md Outdated
There have been a couple places where flow types have been difficult to get right.

1. **contravariants** `this.toggleFramesDisplay = this.toggleFramesDisplay.bind(this)`
2. **defaultProps** and other class properties.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

What was the difficulty with defaultProps?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

adding static props in the class definition. I'll add that

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I would also like to know what was difficult

We are not currently typing our test files, but I think it would be helpful if
we did. In this example, we are searching for all of the functions in a given
source, but because we did not provide a type for `getSourceText`, flow can
not use `getSymbols`'s types to tell us if we're writing a reasonable test.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The only caveat here is that it's sometimes useful to be able to "cheat" in tests, like pass in a mock object that doesn't match the full type but does just what this particular unit test needs.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

true, I'm finding that now...

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Generally type systems eliminate the need to do things like that. But you can do one of two things in that scenario:

  1. Use Flow's suppress_comment option
  2. Pass the value through any to stop type checking – I'd recommend doing this along with a comment explaining why it any is needed. Using any means you're less tied to configuration
function method(value: string) {
  if (typeof value === 'number') throw new TypeError('Support for numbers has been removed');
  return value;
}

test('throws deprecation error', () => {
  expect(() => {
    // Passing through `any` because we want to make sure we're throwing an error.
    method((42: any));
  }).toThrow(TypeError);
});

breakpoints: I.Map<string, Breakpoint>,
pendingBreakpoints: any[],
breakpointsDisabled: false
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

There are still a lot of anys, especially for callback functions and items stored in Immutable structures. Might be good to either review if they are trivially removable or, if there are real impediments, make a list of what they are.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes - there are. We should file bugs based on some of these next steps

Comment thread docs/updates/5-1-2017-tests.md Outdated
* **80+** coverage
* **60** files of coverage

We use Jest to test our react Component. We mostly do snapshot testing to

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

React components?

2. **defaultProps** and other class properties.

Overall, typing our components has been a nice win though as it gives us additional confidence our UI will behave appropriately and catches several lifecycle edge-cases
where properties could be null or racey.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

racey?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think this means that some related data can be set in a non-deterministic order based on how async actions happen to fire. (Especially true for things like parsing that happen in a separate web worker thread.) Flow makes us declare which things have to be present together and prove that all possible state transitions meet those rules.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Might want to explain it a bit more

@codecov

codecov Bot commented May 28, 2017

Copy link
Copy Markdown

Codecov Report

❗ No coverage uploaded for pull request base (master@2cd9631). Click here to learn what that means.
The diff coverage is n/a.

Impacted file tree graph

@@            Coverage Diff            @@
##             master    #2780   +/-   ##
=========================================
  Coverage          ?   47.59%           
=========================================
  Files             ?       97           
  Lines             ?     4036           
  Branches          ?      828           
=========================================
  Hits              ?     1921           
  Misses            ?     2115           
  Partials          ?        0

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 2cd9631...f7e7874. Read the comment docs.

@jasonLaster jasonLaster merged commit f017ad6 into firefox-devtools:master Jun 14, 2017
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.

4 participants