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

feat: Depreciate toBeInTheDOM with replacement #40

Merged
merged 16 commits into from
Jul 18, 2018
Merged

feat: Depreciate toBeInTheDOM with replacement #40

merged 16 commits into from
Jul 18, 2018

Conversation

smacpherson64
Copy link
Collaborator

What:
Deprecating toBeInTheDOM
Adding toBeInTheDocument

Why:
Aiming to implement desires from conversation in issue #34

How:
Added a deprecate function to warn of deprecation
Added jsdoc to types to label as deprecated
Added toBeInTheDocument function
Updated documentation to have deprecated notices
Added toBeInTheDocument to documentation

Checklist:

  • Documentation
  • Tests
  • Updated Type Definitions
  • Ready to be merged
  • Added myself to contributors table

@gnapse, @jgoz, and @kentcdodds Here is the first attempt to do the changes we discussed in #34, when you have time please review. :-) !

@smacpherson64 smacpherson64 requested review from gnapse and jgoz July 14, 2018 22:07
Copy link
Collaborator

@jgoz jgoz left a comment

Choose a reason for hiding this comment

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

Implementation of toBeInTheDocument looks good, just a couple minor suggestions from me.

src/utils.js Outdated
console.warn(message)
}

function getDepreciationMessage(name, replacementText) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Does this need to be exposed as a separate function at all? I know it's used in the tests, but I would rather see the full message typed out in the tests than use this function in both spots. Otherwise we never really test the output of this function.

My preference would be to remove this function and inline the message creation in deprecate:

function deprecate(name, replacementText) {
  // Notify user that they are using deprecated functionality.
  // eslint-disable-next-line no-console
  console.warn(
    `Warning: ${name} has been deprecated and will be removed in future updates.`,
     replacementText
  )
}

Copy link
Collaborator Author

@smacpherson64 smacpherson64 Jul 14, 2018

Choose a reason for hiding this comment

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

Sounds good, I will aim to remove this and update the check to use the text.

test('deprecate', () => {
const spy = jest.spyOn(console, 'warn').mockImplementation(() => {})

const messageWithReplacement = getDepreciationMessage('test', 'test')
Copy link
Collaborator

Choose a reason for hiding this comment

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

As I commented below, I think we should type out the full expected message here, rather than rebuilding it again with the function.

checkHtmlElement(element, toBeInTheDocument, this)

return {
pass: document.documentElement.contains(element),
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should we check whether document and document.documentElement exist? I know jsdom will have them, but if I accidentally used this matcher in the node environment, it would be nice to get a descriptive error message rather than "cannot read property 'documentElement' of undefined".

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Hrm, sounds good, agreed on developer experience. It would be nice to get a clear error. Does the following sound good @jgoz?

document is undefined on global but is required for the toBeInTheDocument matcher.

documentElement is undefined on document but is required for the toBeInTheDocument matcher.

Copy link
Collaborator

Choose a reason for hiding this comment

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

👍 the first message should be good enough for both cases.

@smacpherson64
Copy link
Collaborator Author

@jgoz Thanks for the great feedback!

Copy link
Collaborator

@jgoz jgoz left a comment

Choose a reason for hiding this comment

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

Changes & proposed changes look good to me! Just one remaining nitpick in the readme.

(Btw, I'm heading on vacation for a few days and won't have internet connectivity during that time.)

README.md Outdated
```

This allows you to assert whether an element has content or not.
This allows you to assert whether an element present in the document or not.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Missing is before present

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Nice find! Kk, fixed. (I hope you have an awesome vacation!)

@smacpherson64
Copy link
Collaborator Author

smacpherson64 commented Jul 15, 2018

@jgoz: here is an update on better error message on document for developer experience:

TDLR

I am not sure that jest-dom can be used without the jsdom environment. I get an error when trying to test without document:

... jest-dom\node_modules\jsdom\lib\jsdom\browser\Window.js:598
      window.document.addEventListener("load", () => {

I believe it is pulled in here:
https://github.com/gnapse/jest-dom/blob/master/jest.config.js#L4

Details

To get a better error message when the document is missing, I created a checkDocumentKey utility similar to checkHtmlElement:

function checkDocumentKey(key, matcherFn) {
  if (typeof document === 'undefined') {
    throw new InvalidDocumentError(
      `document is undefined on global but is required to use ${
        matcherFn.name
      }.`,
      matcherFn,
    )
  }

  if (typeof document[key] === 'undefined') {
    throw new InvalidDocumentError(
      `${key} is undefined on document but is required to use ${
        matcherFn.name
      }.`,
      matcherFn,
    )
  }
}

I can successfully get all the tests to pass however, getting coverage up to 100% requires testing without the document. When I remove the document from global, the util test throws an error.

Test that Caused Error

describe('checkDocumentKey without document', () => {
  beforeEach(() => {
    global.previousDocument = {...document}
    delete global.document
  })

  test('checkDocumentkey', () => {
    expect(() =>
      checkDocumentKey('documentElement', matcherMock),
    ).toThrowError()
  })

  afterEach(() => {
    global.document = global.previousDocument
    delete global.previousDocument
  })
})

Error

... jest-dom\node_modules\jsdom\lib\jsdom\browser\Window.js:598
      window.document.addEventListener("load", () => {

@smacpherson64
Copy link
Collaborator Author

smacpherson64 commented Jul 15, 2018

Well, I was wrong about getting coverage to 100%, haha there is always another way. @jgoz I have updated the concept and it should now check for undefined document or a specific key on document.

I am still not sure if jest-dom can run without jsdom.

README.md Outdated
expect(queryByTestId(container, 'not-empty')).not.toBeEmpty()
// <span data-testid="count-value">2</span>
expect(queryByTestId(container, 'count-value').toBeInTheDocument()
expect(queryByTestId(container, 'count-value1')).not.toBeInTheDocument()
Copy link
Member

@gnapse gnapse Jul 16, 2018

Choose a reason for hiding this comment

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

I think that expect(nullOrUndefined).not.toBeInTheDocument() would fail before even having the chance to actually check for presence in the document. So this second example is not accurate because queryByTestId(container, 'count-value1') will return null or undefined, and neither of those is a html or svg element.

So in this sense, it's not a direct replacement for toBeInTheDOM, and I think that's a good thing. The migration path is not simply "replace all uses of toBeInTheDOM with toBeInTheDocument.

Not sure though on how to approach this in the README to put examples of both a positive and a negative test case passing. And also I think we need a test that asserts that toBeInTheDocument indeed fails when given null or undefined.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Sounds Good! I will work to take care of these!

Thank you @gnapse!

@smacpherson64
Copy link
Collaborator Author

smacpherson64 commented Jul 17, 2018

Hey @gnapse!

I believe the following should be complete:

  • Updated the README to have better examples for toBeInTheDocument (using examples from the tests).
  • Added a disclaimer aimed to emphasize that toBeInTheDocument is not a direct replacement of toBeInTheDOM.
  • Added null and undefined tests

Please let me know of any other change you desire!

README.md Outdated
// const htmlElement = document.querySelector('[data-testid="html-element"]')
// const svgElement = document.querySelector('[data-testid="html-element"]')
// const detachedElement = document.createElement('div')

expect(queryByTestId(container, 'count-value').toBeInTheDocument()
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've thought this line was also going to be replaced to use some of the other elements you define above that are indeed in the document.

I'd even insist in changing it merely on the ground that this is referencing a helper function in dom-testing-library, probably since this README was created based on that other library's README.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I am sorry! Great catch!

@smacpherson64
Copy link
Collaborator Author

@gnapse again, Great catch!

Do the updates match what you were desiring?

Copy link
Member

@gnapse gnapse left a comment

Choose a reason for hiding this comment

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

LGTM!

@gnapse
Copy link
Member

gnapse commented Jul 19, 2018

Hmmm, merging this PR did not create a new release in npm, even though in the releases page it seems like something did happen. The latest two releases appear but just as tags, not as full features releases. And indeed, in npm the two new releases do not appear. It's stuck at v1.8.1.

I sense this has something to do with the recent tokens invalidations in npmjs.com, but I'm not sure how to update semantic-release's token. Care to chime in @kentcdodds?

@kentcdodds
Copy link
Member

Oh yes, I manually generated a new token on npm and updated the NPM_TOKEN variable on all my Travis jobs. Took a little bit of time (I have quite a few packages) but wasn't too much of a challenge. Let me know if you need help.

gnapse added a commit that referenced this pull request Jul 19, 2018
There was an issue with a minor release, so this manual-releases.md
change is to release a new minor version.

Reference: #40
@gnapse
Copy link
Member

gnapse commented Jul 19, 2018

Thanks for your help @kentcdodds! Yes, that worked. Just needed the guidance on where to change the token. We have a v1.10.0 release now 🎉.

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