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

Refactor test helpers #1336

Merged
merged 25 commits into from Jul 22, 2018
Merged

Refactor test helpers #1336

merged 25 commits into from Jul 22, 2018

Conversation

ylemkimon
Copy link
Member

@ylemkimon ylemkimon commented May 21, 2018

  • Combine common codes from parse and build, and dispatch using Mode
  • Remove toNotBuild and toNotParse and use expect.not
  • Support tagging literals: Refactor test helpers #1336 (comment)
  • Improve failed test message:
    • Use color
    • Print stack traces
    • Print diff
    • Follow jest matcher output style
expect tests success ParseError Warning other errors
expect.toParse parse O X X X
expect.not.toParse parse X O O X
expect.toBuild build O X X X
expect.not.toBuild build X O O X
expect.toFailWithParseError parse X O* X X
expect.not.toFailWithParseError parse O X** O X
expect.toWarn build X X O X
expect.not.toWarn build O O X X

(O = pass, X = fail)
* only if matches given string, if given
** only if doesn't match given string, if given

* Combine common codes from `parse` and `build`, and dispatch using
`Mode`
* Remove `toNotBuild` and `toNotParse` and use `expect.not`
* Remove `Warning` and instead mock `console.warn`
* Add `expect.toHavePassed` and check whether parse/build succeeded
lazily
* Improve failed test `message`:
  - Use color
  - Print stack traces(excluding internals)
  - Print diff
  - Follow jest matcher output style
@codecov
Copy link

codecov bot commented May 21, 2018

Codecov Report

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

Impacted file tree graph

@@            Coverage Diff            @@
##             master    #1336   +/-   ##
=========================================
  Coverage          ?   83.87%           
=========================================
  Files             ?       79           
  Lines             ?     4539           
  Branches          ?      797           
=========================================
  Hits              ?     3807           
  Misses            ?      639           
  Partials          ?       93

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 c994722...c64ef13. Read the comment docs.

@edemaine
Copy link
Member

The advantage of the previous global warning -> error conversion is that we could make sure no code produced warnings when it wasn't supposed to. In fact, I was going to suggest changing the default strict setting to the actual default, instead of strict: false, to make sure no warnings were generated when they weren't supposed to. I guess such warnings would be printed to console, but it wouldn't prevent a PR from merging, for example. What do you think?

@ylemkimon

This comment has been minimized.

getParsed throws an error if parsing fails.
@ylemkimon ylemkimon changed the title Refactor test helpers [WIP]Refactor test helpers May 25, 2018
@kevinbarabash
Copy link
Member

@ylemkimon are you planning changes or should I review?

@ylemkimon

This comment has been minimized.

@kevinbarabash kevinbarabash changed the title [WIP]Refactor test helpers Refactor test helpers May 28, 2018
@edemaine
Copy link
Member

@ylemkimon I'm no longer in agreement on the warnings. Running from the command-line, I'd see warnings, but if they don't cause tests to fail, Travis will still allow the merge, which it shouldn't. Real-life example: In #1385 discussion, we spotted a bug through the existing warning -> error mechanism that we may not have noticed otherwise.

@kevinbarabash
Copy link
Member

@edemaine is there an action here for @ylemkimon? I'm not sure what the next step is for this PR.

@edemaine
Copy link
Member

edemaine commented Jun 5, 2018

  1. I would turn warnings back into errors, but that's open to discussion.
  2. Someone needs to actually review the code; I haven't yet.
  3. Resolve conflicts (sorry!).
  4. @ylemkimon wanted to add some literal magic.

@kevinbarabash
Copy link
Member

Since it sounds like there's still some changes pending I'm going to mark this as "needs-revision".

@ylemkimon
Copy link
Member Author

This is ready for review.

@kevinbarabash kevinbarabash self-assigned this Jun 27, 2018
@kevinbarabash
Copy link
Member

I'm going to try to look at this this weekend. I should've suggested saving the tagged template change for a separate PR.

@ylemkimon
Copy link
Member Author

ylemkimon commented Jun 27, 2018

@kevinbarabash I agree. For review:

const parseB = stripPositions(getParsed("x_3^2"));

expect(parseA).toEqual(parseB);
expect`x^2_3`.toParseLike`x_3^2`;
Copy link
Member Author

@ylemkimon ylemkimon Jun 27, 2018

Choose a reason for hiding this comment

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

Changed to use toParseLike. (b3dcf40)

expect("\text{hello% comment\n\nworld}")
.toParseLike("\text{hello world}");
expect("\\text{hello% comment 1\nworld}").toParseLike`\text{helloworld}`;
expect("\\text{hello% comment\n\nworld}").toParseLike`\text{hello world}`;
Copy link
Member Author

@ylemkimon ylemkimon Jun 27, 2018

Choose a reason for hiding this comment

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

There were some cases of using single backslash with t resulting in a horizontal tab(\t). (17ca384)

@kevinbarabash
Copy link
Member

@ylemkimon thanks for group the commits into logical groupings. I'll look at the individual commits when I do this review.

Copy link
Member

@kevinbarabash kevinbarabash left a comment

Choose a reason for hiding this comment

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

@ylemkimon thanks for taking a stab at this. It needs some discussion.

@@ -6,76 +6,76 @@ describe("Parser:", function() {

describe("#handleInfixNodes", function() {
it("rejects repeated infix operators", function() {
expect("1\\over 2\\over 3").toFailWithParseError(
expect`1\over 2\over 3`.toFailWithParseError(
Copy link
Member

Choose a reason for hiding this comment

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

This looks a little strange to me b/c I'm not used to template strings returning things that aren't strings. Maybe we could introduce a tex template literal function. It would look like:

expect(tex`1\over 2\over 3`).toFailWithParseError(...

The tex function could be exported for use by other people. Also, it would be easier to understand what's going on. I'm still not sure how expect is acting as a template function without us modifying/replacing it with our own implementation.

Copy link
Member Author

Choose a reason for hiding this comment

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

@kevinbarabash I agree that it can be unfamiliar since it's new syntax. However, it's new way to call the function with arguments: an array of strings with raw property, an array of raw strings, and expressions in ${}. As expect accepts any object, it just passes the first argument through and we can handle them in the matchers. Maybe we can document them in the CONTRIBUTING.md?

Copy link
Member

@edemaine edemaine Jul 8, 2018

Choose a reason for hiding this comment

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

If expect passes through only one argument, does that mean this won't work with ${interpolated expressions}?

I don't think the new code looks much more confusing than the old code -- the weirdest part is that expect just passes on its argument, and the action is in the chained methods afterward -- but that's a general Jest thing. However, until I read the template literal description I didn't really understand what the code was doing.

What about expectTex`1 \over 2 \over 3` which is equivalent to expect(tex`1 \over 2 \over 3`), i.e., the string gets processed before calling expect, instead of requiring all matches to process it? I think the weirdest part is overloading expect in this unintended way (unless Jest suggests doing this?).

Copy link
Member Author

@ylemkimon ylemkimon Jul 8, 2018

Choose a reason for hiding this comment

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

@edemaine Yes, it will throw an error if there are ${interpolated expressions} and r will return the part before the first one.

I think we can think of expect.toParse as expect the raw string of the tagged literal to success parsing.

Copy link
Member Author

@ylemkimon ylemkimon Jul 8, 2018

Choose a reason for hiding this comment

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

It's not related, but Jest provides a way of creating a table using tagged template literal:

describe.each`
  a    | b    | expected
  ${1} | ${1} | ${2}
  ${1} | ${2} | ${3}
  ${2} | ${1} | ${3}
`('$a + $b', ({a, b, expected}) => {
  test(`returns ${expected}`, () => {
    expect(a + b).toBe(expected);
  });

  test(`returned value not be greater than ${expected}`, () => {
    expect(a + b).not.toBeGreaterThan(expected);
  });

  test(`returned value not be less than ${expected}`, () => {
    expect(a + b).not.toBeLessThan(expected);
  });
});

Copy link
Member

Choose a reason for hiding this comment

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

Interesting. I was unaware of jest's use of tagged template literals. expectTex seems like a good compromise.

Copy link
Member

Choose a reason for hiding this comment

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

@ylemkimon apologies for not raising my concerns about expect as a tagged template earlier.

Copy link
Member Author

@ylemkimon ylemkimon Jul 9, 2018

Choose a reason for hiding this comment

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

@kevinbarabash @edemaine

@ylemkimon apologies for not raising my concerns about expect as a tagged template earlier.

I don't mind. I think productive discussion is necessary.

What about expectTex`1 \over 2 \over 3` which is equivalent to expect(tex`1 \over 2 \over 3`), i.e., the string gets processed before calling expect, instead of requiring all matches to process it? I think the weirdest part is overloading expect in this unintended way (unless Jest suggests doing this?).

Interesting. I was unaware of jest's use of tagged template literals. expectTex seems like a good compromise.

I still disagree. The purpose of using tagged template literal is to avoid using escape backslashes and simplify test cases. I think using expectTeX defeats this purpose as there is no difference between processing before passing to expect and processing in the matchers, when using KaTeX matchers.

Copy link
Member

Choose a reason for hiding this comment

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

@ylemkimon There would be a difference if we needed to use Jest's matchers. But I can't actually think of any situation where we'd want to use those on a TeX string.

/**
* Return the first raw string if x is tagged literal. Otherwise return x.
*/
export const r = x => x != null && x.hasOwnProperty('raw') ? x.raw[0] : x;
Copy link
Member

Choose a reason for hiding this comment

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

This function needs a better name.

Copy link
Member Author

Choose a reason for hiding this comment

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

@kevinbarabash I borrowed it from python's raw string prefix. Maybe tex, as you suggested above?

Copy link
Member

Choose a reason for hiding this comment

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

I wonder if people will want to use it like

tex`\frac{${num}}{${den}}`

That could be a separate PR though if people think that's worth supporting.

Copy link
Member Author

Choose a reason for hiding this comment

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

@kevinbarabash Wouldn't it be same as String.raw?

Copy link
Member

Choose a reason for hiding this comment

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

I supposed it would be in which case I guess people can just use String.raw. 😅

test/helpers.js Outdated
try {
return parseTree(expr, settings);
expect(result).toHavePassed();
Copy link
Member

Choose a reason for hiding this comment

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

I would expect getTree to call katex.render() and return the tree or clean up the stack trace and rethrow as necessary. Then expectKaTeX can call getTree and we don't have introduce a special matcher just to make things work in this order. Is there something I'm missing that necessitates this setup?

test/helpers.js Outdated
};

export const buildAndSetResult = function(expr, result,
settings = new Settings()) {
export const expectKaTeX = (expr, settings = new Settings(), mode, isNot,
Copy link
Member

Choose a reason for hiding this comment

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

Does the default settings work when undefined is passed as the second arg?

Copy link
Member

Choose a reason for hiding this comment

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

I really like that we're supporting .not now, but this helper function has complicated the implementation quite a bit. I'd rather have a few small helper functions that each of the toParse(), toBuild(), etc. call then a single function that tries to do it all. Maybe we could add something format the message in addition to the function you added to clean up the stack trace.

Copy link
Member Author

Choose a reason for hiding this comment

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

@kevinbarabash

Does the default settings work when undefined is passed as the second arg?

Yes. Default function parameters allow formal parameters to be initialized with default values if no value or undefined is passed.

* Move `_getBuilt` into `getBuilt`
* Move default settings and tagging literal support in to `getParsed` 
and `getBuilt`
* Remove stack traces clean-up
* Remove `toHavePassed` matcher
@ylemkimon
Copy link
Member Author

ylemkimon commented Jul 8, 2018

@kevinbarabash

I would expect getTree to call katex.render() and return the tree or clean up the stack trace and rethrow as necessary. Then expectKaTeX can call getTree and we don't have introduce a special matcher just to make things work in this order.

Yes, I agree. My original intention was to reuse the error message from expectKaTeX and clean-up stack traces. However, now I think, they are not needed, they made codes unnecessarily complicated, and even made errors hard to debug.

I've removed getTree in f783eb8.

I really like that we're supporting .not now, but this helper function has complicated the implementation quite a bit. I'd rather have a few small helper functions that each of the toParse(), toBuild(), etc. call then a single function that tries to do it all. Maybe we could add something format the message in addition to the function you added to clean up the stack trace.

With removing getTree, I think expectKaTeX became simple enough. Maybe we can extract expected string construction into separate function, like printActualErrorMessage?

@kevinbarabash
Copy link
Member

With removing getTree, I think expectKaTeX became simple enough. Maybe we can extract expected string construction into separate function, like printActualErrorMessage?

Sounds good.

@ylemkimon
Copy link
Member Author

With removing getTree, I think expectKaTeX became simple enough. Maybe we can extract expected string construction into separate function, like printActualErrorMessage?

Sounds good.

Done in fc47230.

Copy link
Member

@kevinbarabash kevinbarabash left a comment

Choose a reason for hiding this comment

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

Sorry for letting this sit for so long. Let's get this merged.

@kevinbarabash kevinbarabash merged commit 71035c7 into KaTeX:master Jul 22, 2018
@ylemkimon ylemkimon deleted the jest-test-helpers branch August 10, 2018 10:43
@ylemkimon ylemkimon mentioned this pull request Aug 18, 2018
1 task
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants