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

Test Thoughts & Subthoughts #312

Merged
merged 1 commit into from
Mar 1, 2020

Conversation

mekanix
Copy link
Contributor

@mekanix mekanix commented Jan 30, 2020

Refers to #244

@mekanix
Copy link
Contributor Author

mekanix commented Jan 31, 2020

Only first test is passing, no matter which test it is. I'm suspecting I need to clean up something or reinit firebase or something, so I'm closing this PR for now.

@mekanix mekanix closed this Jan 31, 2020
@mekanix mekanix reopened this Jan 31, 2020
@mekanix
Copy link
Contributor Author

mekanix commented Jan 31, 2020

Everything works if we have one it section per test file. As test file naming scheme doesn't have to follow component naming, we can have as many test files as we want, so I think it's an acceptable trade-off.

@raineorshine
Copy link
Contributor

I am unfortunately getting the following error when I try to run the tests:

> [email protected] test /Users/raine/projects/em
> react-scripts test --env=jsdom --silent

2020-02-03 11:54 node[66157] (FSEvents.framework) FSEventStreamStart: register_with_server: ERROR: f2d_register_rpc() => (null) (-22)
2020-02-03 11:54 node[66157] (FSEvents.framework) FSEventStreamStart: register_with_server: ERROR: f2d_register_rpc() => (null) (-22)
2020-02-03 11:54 node[66157] (FSEvents.framework) FSEventStreamStart: register_with_server: ERROR: f2d_register_rpc() => (null) (-22)
events.js:187
      throw er; // Unhandled 'error' event
      ^

Error: EMFILE: too many open files, watch
    at FSEvent.FSWatcher._handle.onchange (internal/fs/watchers.js:123:28)
Emitted 'error' event on FSWatcher instance at:
    at FSEvent.FSWatcher._handle.onchange (internal/fs/watchers.js:129:12) {
  errno: -24,
  syscall: 'watch',
  code: 'EMFILE',
  filename: null
}
npm ERR! Test failed.  See above for more details.

@raineorshine
Copy link
Contributor

Looking into a solution, as it may be OSX specific.

facebook/create-react-app#4540

@raineorshine
Copy link
Contributor

Sweet, installing watchman was all it took to fix that.

Copy link
Contributor

@raineorshine raineorshine left a comment

Choose a reason for hiding this comment

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

I still have not figured out how to run more than one it block in the same file. It seems to not be properly cleaning up/unmounting, because the wrappers are returning nodes from the previous it. It's good that it is not cleaned up automatically, as we want the same component to be used across multiple it blocks when we are testing various properties without having to recreate the DOM each time, but I am not sure why unmount is not working. It would be nice to be able to reset for each describe block in some cases.

I have yet to test this potential solution out: enzymejs/enzyme#911

Another approach: uyhcire/tabliner@3e4c7d3

For now, we can do each full test scenario in a separate file.

src/util/initEvents.js Outdated Show resolved Hide resolved
src/components/__tests__/EmptyThought.js Outdated Show resolved Hide resolved
package.json Outdated Show resolved Hide resolved
@mekanix
Copy link
Contributor Author

mekanix commented Feb 4, 2020

Sweet, installing watchman was all it took to fix that.

Do you want me to add it as dev dependency?

@mekanix mekanix force-pushed the feature/test-thought branch from 35fad43 to 8d80593 Compare February 4, 2020 13:25
@raineorshine
Copy link
Contributor

Do you want me to add it as dev dependency?

No, too heavy. Developers can install it if needed for now.

@mekanix mekanix force-pushed the feature/test-thought branch from 2e66407 to 88cfaca Compare February 6, 2020 09:48
@mekanix
Copy link
Contributor Author

mekanix commented Feb 6, 2020

I left --env=jest in the package.json as without it there is no window or document objects, although there's a section in the .eslintrc.json that clearly states:

  "env": {
    "browser": true,
    "es6": true,
    "jest": true,
    "mocha": true
  },

src/components/App.js Outdated Show resolved Hide resolved
wrapper = null
})

it('create and edit empty thought', async () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Create and edit should be separate tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Think about it, it's useless. In order to edit the thought, you need to create it, so why not test it? Same for delete.

Copy link
Contributor

Choose a reason for hiding this comment

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

I disagree. If there is one test for creating a thought and for editing a thought, and the test fails after some changes are made in the future, we won't know whether there was a regression in creating a thought or editing a thought. While editing a thought will never work if creating a thought is broken, the inverse is not true. Having separate tests isolates these test cases and prevents this ambiguity from arising.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

But failing test tells you exact line where it broke, so we will know what exactly failed. If you still want me to have create and edit, no problem.

Copy link
Contributor

Choose a reason for hiding this comment

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

Good point. Either one is a reasonable approach. Let's go with separate tests since that is my preference.

src/components/__tests__/EmptyThought.js Outdated Show resolved Hide resolved
src/components/__tests__/EmptyThought.js Outdated Show resolved Hide resolved
src/components/__tests__/EmptyThought.js Outdated Show resolved Hide resolved
src/components/__tests__/SubThought.js Outdated Show resolved Hide resolved
src/components/__tests__/SubThought.js Outdated Show resolved Hide resolved
@mekanix mekanix force-pushed the feature/test-thought branch from a80dfe4 to dd15d4b Compare February 7, 2020 16:10
Copy link
Contributor

@raineorshine raineorshine left a comment

Choose a reason for hiding this comment

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

Looking good, thanks!

Could you find a way to get rid of the render warning that appears in the tests?

Warning: render(): Rendering components directly into document.body is discouraged, since its children are often manipulated by third-party scripts and browser extensions. This may lead to subtle reconciliation issues. Try rendering into a container element created for your app.

I will fix the Failed prop type warning.

We should investigate why restoreSelection is failing as well.

const skipTutorial = wrapper.find('div.modal-actions div a')
skipTutorial.simulate('click')
const keyboardResponder = wrapper.find('#keyboard')
await keyboardResponder.simulate('keydown', { key: 'Enter' })
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we put the Enter event in the tests? I'm okay with the skipTutorial click happening here, but the new thought should go in each test. If a regression is introduced to creating a new thought, we want the newThought test to fail, not setupTests.

Copy link
Contributor

Choose a reason for hiding this comment

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

Still waiting on this

}

await act(async () => {
// eslint-disable-next-line no-undef
Copy link
Contributor

Choose a reason for hiding this comment

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

I believe you can remove the eslint-disable lines related to jest now that we have jest specified as an eslint env.

@@ -0,0 +1,6 @@
it('create thought', async () => {
const thought = document.wrapper.find(
'li.leaf div.thought-container div.thought div.editable',
Copy link
Contributor

Choose a reason for hiding this comment

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

Based on my tests, document.wrapper.find('div.editable') works fine.

.editable does not work because there is both a <ContentEditable className='editable'> and a <div className='editable'>

src/components/__tests__/ThoughtDelete.js Show resolved Hide resolved
await thought.update()
expect(thought.text()).toBe('c')
// eslint-disable-next-line fp/no-let
const subthoughtsData = getThoughtsRanked(['c'])
Copy link
Contributor

Choose a reason for hiding this comment

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

These variables are short enough that they can be inlined in the expect.

await thought.update()
expect(thought.text()).toBe('c')
// eslint-disable-next-line fp/no-let
let subthoughtsData = getThoughtsRanked(['c'])
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's avoid let here, and simply inline these short statements as needed.

jest.runAllTimers()
await thought.update()
expect(thought.text()).toBe('c')
// eslint-disable-next-line fp/no-let
Copy link
Contributor

Choose a reason for hiding this comment

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

Similar

jest.runAllTimers()
await thought.update()
expect(thought.text()).toBe('c')
// eslint-disable-next-line fp/no-let
Copy link
Contributor

Choose a reason for hiding this comment

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

Also here

@mekanix mekanix force-pushed the feature/test-thought branch from 071f35e to 0e7f5db Compare February 8, 2020 22:06
@raineorshine
Copy link
Contributor

Also let's move the __tests__ folder up a level since technically we are testing more than components.

@mekanix
Copy link
Contributor Author

mekanix commented Feb 10, 2020

I tried moving tests one directory up, but this is what I end up with:

> [email protected] lint /usr/src/em
> eslint src
​
​
> [email protected] test /usr/src/em
> react-scripts test --env=jsdom
​
/usr/src/em/node_modules/react-scripts/scripts/test.js:20
  throw err;
  ^
​
Error: The `document` global was defined when React was initialized, but is not defined anymore. This can happen in a test environment if a component schedules an update from an asynchronous callback, but the test has already finished running. To solve this, you can either unmount the component at the end of your test (and ensure that any asynchronous operations get canceled in `componentWillUnmount`), or you can change the test itself to be asynchronous.
    at Object.invokeGuardedCallbackDev (/usr/src/em/node_modules/react-dom/cjs/react-dom.development.js:298:17)
    at invokeGuardedCallback (/usr/src/em/node_modules/react-dom/cjs/react-dom.development.js:440:31)
    at commitRootImpl (/usr/src/em/node_modules/react-dom/cjs/react-dom.development.js:25020:9)
    at unstable_runWithPriority (/usr/src/em/node_modules/scheduler/cjs/scheduler.development.js:697:12)
    at runWithPriority$2 (/usr/src/em/node_modules/react-dom/cjs/react-dom.development.js:12149:10)
    at commitRoot (/usr/src/em/node_modules/react-dom/cjs/react-dom.development.js:24922:3)
    at finishSyncRender (/usr/src/em/node_modules/react-dom/cjs/react-dom.development.js:24329:3)
    at performSyncWorkOnRoot (/usr/src/em/node_modules/react-dom/cjs/react-dom.development.js:24307:9)
    at /usr/src/em/node_modules/react-dom/cjs/react-dom.development.js:12199:24
    at unstable_runWithPriority (/usr/src/em/node_modules/scheduler/cjs/scheduler.development.js:697:12)
    at runWithPriority$2 (/usr/src/em/node_modules/react-dom/cjs/react-dom.development.js:12149:10)
    at flushSyncCallbackQueueImpl (/usr/src/em/node_modules/react-dom/cjs/react-dom.development.js:12194:7)
    at flushSyncCallbackQueue (/usr/src/em/node_modules/react-dom/cjs/react-dom.development.js:12182:3)
    at batchedUpdates$1 (/usr/src/em/node_modules/react-dom/cjs/react-dom.development.js:24392:7)
    at Object.notify (/usr/src/em/node_modules/react-redux/lib/utils/Subscription.js:29:7)
    at Subscription.notifyNestedSubs (/usr/src/em/node_modules/react-redux/lib/utils/Subscription.js:71:20)
    at Subscription.handleChangeWrapper (/usr/src/em/node_modules/react-redux/lib/utils/Subscription.js:76:12)
    at dispatch (/usr/src/em/node_modules/redux/lib/redux.js:228:7)
    at Object.dispatch (/usr/src/em/node_modules/redux-thunk/lib/index.js:14:16)
    at /usr/src/em/src/util/loadLocalState.js:111:18
    at processTicksAndRejections (internal/process/task_queues.js:97:5)
npm ERR! Test failed.  See above for more details.

@mekanix mekanix force-pushed the feature/test-thought branch from c6e3dd5 to 700d892 Compare February 11, 2020 09:18
@raineorshine
Copy link
Contributor

I tried moving tests one directory up, but this is what I end up with

Okay, that's too bad.

@raineorshine
Copy link
Contributor

Thank you, those are perfectly sized and named commits.

@mekanix mekanix force-pushed the feature/test-thought branch from 700d892 to facb4d6 Compare February 19, 2020 16:25
@raineorshine
Copy link
Contributor

Well, after a couple hours of trying different things myself, I was also unable to get the Editable element to update in the unit test. None of the tips in enzymejs/enzyme#76 seemed to work.

That said, the change event works great and the store is updated, so we can call getThoughts which accesses the store directly to see the changed value. Will you utilize this approach in all unit tests?

import { ROOT_TOKEN } from '../../constants.js'
import { getThoughts } from '../../util.js'

it('edit thought', async () => {
  const thought = document.wrapper.find('div.editable')
  expect(thought.text()).toBe('')

  await thought.simulate('change', { target: { value: 'c' } })
  jest.runAllTimers()

  // for some reason the text of the Editable element is not updated
  // await document.wrapper.update()
  // expect(document.wrapper.find('div.editable').text()).toBe('c')

  const children = getThoughts([ROOT_TOKEN])
  expect(children).toHaveLength(1)
  expect(children[0]).toMatchObject({ value: 'c', rank: 0 })
})

@raineorshine
Copy link
Contributor

These changes are important to set EDIT_THROTTLE = 0 during testing and fix the auto merge:

766e1a6

@mekanix mekanix force-pushed the feature/test-thought branch from e1484cc to 12f5dd4 Compare February 21, 2020 10:27
src/components/__tests__/SubThoughtCreate.js Show resolved Hide resolved
src/components/__tests__/SubThoughtDelete.js Outdated Show resolved Hide resolved
src/components/__tests__/SubThoughtEdit.js Outdated Show resolved Hide resolved
src/components/__tests__/SubThoughtTop.js Outdated Show resolved Hide resolved
src/components/__tests__/ThoughtDelete.js Outdated Show resolved Hide resolved

await act(async () => {
jest.useFakeTimers()
const originalError = console.error
Copy link
Contributor

Choose a reason for hiding this comment

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

Hey, I'm not really comfortable with disabling console.error to hide the error. It seems like it's just a workaround. Let's try to get to the source of the problem. If it is impossible to remove, it deserves a clear comment explaining why.

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 think https://github.com/facebook/react/blob/master/packages/react-dom/src/client/ReactDOMLegacy.js#L81 is where we get that error from. As I don't see any way to go around it except creating new console.error which filters what it outputs by content (we can output only if first argument to console.error is not the one from the link).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

New console.error mock is more precise in what it silences. Good enough?

Copy link
Contributor

Choose a reason for hiding this comment

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

Mounting to a child of document.body will fix the error without the workaround:

    // mount to child to avoid warning about rendering directly to document.body
    const root = document.body.appendChild(document.createElement('div'))

    const wrapper = await mount(
      <div
        id="keyboard"
        onKeyDown={keyDown}
        tabIndex="0"
      >
        {app}
      </div>,
      { attachTo: root },
    )

@mekanix mekanix force-pushed the feature/test-thought branch 2 times, most recently from e61fe08 to ccf085f Compare February 21, 2020 20:05
@raineorshine
Copy link
Contributor

I was going to try this solution, but this branch is crashing as-is when I run npm run test. Is it working for you?

   Console

    console.warn src/reducers/settings.js:25
      Missing oldThoughtRanked in Settings update: Tutorial Off


 RUNS  src/components/__tests__/SubThoughtCreate.js
/Users/raine/projects/em/node_modules/react-scripts/scripts/test.js:20
  throw err;
  ^

TypeError: Cannot read property '_location' of null
    at Window.get location [as location] (/Users/raine/projects/em/node_modules/jsdom/lib/jsdom/browser/Window.js:148:79)
    at Object.<anonymous>.exports.loadLocalState (/Users/raine/projects/em/src/util/loadLocalState.js:56:32)
npm ERR! Test failed.  See above for more details.

@mekanix
Copy link
Contributor Author

mekanix commented Feb 22, 2020

I do see it in the log, but it doesn't break. This is the whole log on my machine http://dpaste.com/27JD3PE

@mekanix mekanix force-pushed the feature/test-thought branch from d5f8716 to 2d1b58f Compare February 24, 2020 09:49
@raineorshine
Copy link
Contributor

Great! The unmount fixed it. All tests passing for me.

src/components/__tests__/SubThoughtCreate.js Outdated Show resolved Hide resolved
src/components/__tests__/SubThoughtEdit.js Outdated Show resolved Hide resolved
src/components/__tests__/SubThoughtEdit.js Show resolved Hide resolved
src/components/__tests__/SubThoughtTop.js Show resolved Hide resolved
src/components/__tests__/SubThoughtTop.js Show resolved Hide resolved
src/components/__tests__/ThoughtDelete.js Outdated Show resolved Hide resolved
src/components/__tests__/ThoughtAbove.js Outdated Show resolved Hide resolved
src/components/__tests__/SubThoughtCreate.js Outdated Show resolved Hide resolved
const skipTutorial = wrapper.find('div.modal-actions div a')
skipTutorial.simulate('click')
const keyboardResponder = wrapper.find('#keyboard')
await keyboardResponder.simulate('keydown', { key: 'Enter' })
Copy link
Contributor

Choose a reason for hiding this comment

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

Still waiting on this


await act(async () => {
jest.useFakeTimers()
const originalError = console.error
Copy link
Contributor

Choose a reason for hiding this comment

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

Mounting to a child of document.body will fix the error without the workaround:

    // mount to child to avoid warning about rendering directly to document.body
    const root = document.body.appendChild(document.createElement('div'))

    const wrapper = await mount(
      <div
        id="keyboard"
        onKeyDown={keyDown}
        tabIndex="0"
      >
        {app}
      </div>,
      { attachTo: root },
    )

@mekanix
Copy link
Contributor Author

mekanix commented Feb 25, 2020

Github doesn't let me reply on last two comments.

Still waiting on this:
Isn't it more logical to have something in setup that will happen every single time? If not, I'll copy/paste it into tests, but it makes no sense to me.

document.body is not used any more

@raineorshine
Copy link
Contributor

Isn't it more logical to have something in setup that will happen every single time? If not, I'll copy/paste it into tests, but it makes no sense to me.

That's a fair argument I suppose. To me it seems clear that creating a thought is a testable action, not part of setup. It is also more future-proof, as there will likely be tests that do not begin by creating a new thought.

@mekanix mekanix force-pushed the feature/test-thought branch from 86dc39d to b0be19c Compare February 26, 2020 10:02
@raineorshine raineorshine changed the title Test thought Test Thoughts & Subthoughts Mar 1, 2020
@raineorshine raineorshine merged commit 40d8724 into cybersemics:dev Mar 1, 2020
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.

2 participants