-
Notifications
You must be signed in to change notification settings - Fork 124
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
Tests for Tutorial #2702
Tests for Tutorial #2702
Changes from 17 commits
3f522c1
988f092
8496e3a
29d2790
34aa6df
46dcbd6
fb9ecdd
78dce26
098ff17
eedd501
16ff569
8db9c57
fa4dc17
49c3e80
0b8c718
c6499ef
3b2b4f7
56c10ba
cb0df78
7793de4
a42df99
b2971a9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,7 +14,6 @@ const TutorialNavigationButton = React.forwardRef< | |
>(({ clickHandler, value, disabled = false, classes }, ref) => ( | ||
<a | ||
className={cx(anchorButtonRecipe({ variableWidth: true, smallGapX: true }), classes)} | ||
onClick={clickHandler} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it's duplicated by There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, good catch. It doesn't make sense to have both. |
||
{...{ disabled }} | ||
{...fastClick(clickHandler)} | ||
ref={ref} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,296 @@ | ||
import { getByText, screen } from '@testing-library/dom' | ||
import userEvent from '@testing-library/user-event' | ||
import { act } from 'react' | ||
import { | ||
HOME_TOKEN, | ||
TUTORIAL2_STEP_CHOOSE, | ||
TUTORIAL2_STEP_CONTEXT1, | ||
TUTORIAL2_STEP_CONTEXT1_PARENT, | ||
TUTORIAL2_STEP_CONTEXT1_SUBTHOUGHT, | ||
TUTORIAL2_STEP_CONTEXT2_PARENT, | ||
TUTORIAL2_STEP_CONTEXT_VIEW_EXAMPLES, | ||
TUTORIAL2_STEP_CONTEXT_VIEW_OPEN, | ||
TUTORIAL2_STEP_CONTEXT_VIEW_SELECT, | ||
TUTORIAL2_STEP_START, | ||
TUTORIAL_STEP_AUTOEXPAND, | ||
TUTORIAL_STEP_AUTOEXPAND_EXPAND, | ||
TUTORIAL_STEP_FIRSTTHOUGHT, | ||
TUTORIAL_STEP_FIRSTTHOUGHT_ENTER, | ||
TUTORIAL_STEP_SECONDTHOUGHT, | ||
TUTORIAL_STEP_SECONDTHOUGHT_ENTER, | ||
TUTORIAL_STEP_START, | ||
TUTORIAL_STEP_SUBTHOUGHT, | ||
TUTORIAL_STEP_SUBTHOUGHT_ENTER, | ||
TUTORIAL_STEP_SUCCESS, | ||
} from '../../../constants' | ||
import exportContext from '../../../selectors/exportContext' | ||
import store from '../../../stores/app' | ||
import createTestApp, { cleanupTestApp, cleanupTestEventHandlers } from '../../../test-helpers/createTestApp' | ||
|
||
// as per https://testing-library.com/docs/user-event/options/#advancetimers | ||
// we should avoid using { delay: null }, and use jest.advanceTimersByTime instead | ||
const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime }) | ||
|
||
/** Get last created thought in the document, used mostly after `user.keyboard('{Enter}')`. */ | ||
const lastThought = () => Array.from(document.querySelectorAll('[contenteditable="true"]')).at(-1)! | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's use Even better would be to specifically select the empty thought. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I considered this during my previous review, but didn't mention it since the last thought and the empty thought were coincidentally always the same. It would be nicer to explicitly select the thought to be edited (the empty thought), and easier to update in the future if we need to edit another thought during testing. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So, I arrived at /** Get last created empty thought in the document. Gets the last thought that is not empty. Mostly used after `user.keyboard('{Enter}')` to get the new thought. */
const lastThought = () =>
Array.from(document.querySelectorAll('[data-editable="true"]'))
.filter(it => !it.textContent)
.at(-1)! I think it works. Perhaps, a more refined solution could be out there, but it works :D |
||
|
||
/** Ensure we are at the given step of the tutorial. */ | ||
const ensureStep = (step: number) => { | ||
expect(store.getState().storageCache?.tutorialStep).toBe(step) | ||
raineorshine marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
describe('Tutorial 1', async () => { | ||
beforeEach(() => createTestApp({ tutorial: true })) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe I'm missing something, but if the tests are all run sequentially on a single app instance, wouldn't this need to be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I really have not much idea how to make it work with one right now it's run with one db/store/state/whatever, but with cleaning up event listeners that keep attaching I'm really not sure how to make it work with one There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for your reply. I'd like to dig a little deeper into this. The tests are quite readable right now, but it's not clear what state they share between test blocks.
What do you mean by "empty-ish DOM"?
Are you saying that the current implementation recreates an isolated testing environment for each case? Because my understanding is that these tests depend on the current test step being preserved between tests.
The event listeners only keep attaching because you remove them and re-attach them each time.
Is it a sequential run or not? Or some hybrid? I think for clarity we need to stick with one or the other, or more carefully document the unique combination of shared state between test runs. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
we have everything in db, store etc., but dom is empty if we don't call the
Just runing a
but it does not work like this at all. describe.only('T_T', () => {
beforeAll(createTestApp)
it('we create "first" thought', async () => {
await user.keyboard('{Enter}')
await user.type(lastEmptyThought(), 'first')
await user.keyboard('{Enter}')
expect(exportContext(store.getState(), HOME_TOKEN)).toEqual(`<ul>
<li>__ROOT__
<ul>
<li>first</li>
<li></li>
</ul>
</li>
</ul>`)
expect(screen.getByText('first')).toBeInTheDocument()
})
it('we expect to see "first" thought', () => {
console.log(exportContext(store.getState(), HOME_TOKEN))
screen.debug(document.querySelector('.content')!) /* <-- prints this
<body
data-browser="safari"
data-color-mode="dark"
data-device="desktop"
data-drag-in-progress="false"
data-native="false"
data-platform="other"
/>
*/
expect(() => screen.getByText('first')).toThrow()
})
}) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe we should "fix" this, but will it break other tests? i generally feel like this pr now achieves what we want it to, given the current testing environment There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks. I'd love to figure out why the DOM is empty. Without knowing the answer to that, it's hard to know what the right call is here. However, since all the tests pass and they are quite readable, I'm okay with merging the current implementation so that we have a good checkpoint in the commit history. We can investigate further in a separate issue. If you could address the small stylistic changes below, then I'd be happy to merge. Thanks! |
||
afterEach(cleanupTestEventHandlers) | ||
afterAll(cleanupTestApp) | ||
describe('step start', () => { | ||
it('shows the welcome text', () => { | ||
ensureStep(TUTORIAL_STEP_START) | ||
expect(screen.getByText('Welcome to your personal thoughtspace.')).toBeInTheDocument() | ||
}) | ||
|
||
it('shows the "Next" button, that leads to the first step', async () => { | ||
await user.click(screen.getByText('Next')) | ||
expect(screen.getByText(/First, let me show you how to create a new thought/)).toBeInTheDocument() | ||
}) | ||
}) | ||
|
||
it('step first thought - how to create a thought ', async () => { | ||
ensureStep(TUTORIAL_STEP_FIRSTTHOUGHT) | ||
expect(screen.getByText('Hit the Enter key to create a new thought.')).toBeInTheDocument() | ||
await user.keyboard('{Enter}') | ||
expect(screen.getByText('You did it!')).toBeInTheDocument() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Test actions look good and are very readable 👍 |
||
|
||
ensureStep(TUTORIAL_STEP_FIRSTTHOUGHT_ENTER) | ||
expect(screen.getByText('Now type something. Anything will do.')).toBeInTheDocument() | ||
await user.type(lastThought(), 'my first thought') | ||
await user.keyboard('{Enter}') | ||
await act(vi.runOnlyPendingTimersAsync) | ||
}) | ||
|
||
it('step second thought - prompts to add another thought', async () => { | ||
expect(screen.getByText('Well done!')).toBeInTheDocument() | ||
expect(screen.getByText(/Try adding another thought/)).toBeInTheDocument() | ||
ensureStep(TUTORIAL_STEP_SECONDTHOUGHT) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actions should generally be performed in the test that depends on them. So instead of typing and hitting enter in one test and asserting the result in the next, move the typing and hitting enter to the next test where it is asserted. As a general rule, a test should end with an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree with this. Some testing philosophies go further and recommend/require exactly one |
||
|
||
await user.type(lastThought(), 'my second thought') | ||
await user.keyboard('{Enter}') | ||
await act(vi.runOnlyPendingTimersAsync) | ||
|
||
ensureStep(TUTORIAL_STEP_SECONDTHOUGHT_ENTER) | ||
|
||
expect(screen.getByText('Good work!')).toBeInTheDocument() | ||
expect(screen.getByText('Now type some text for the new thought.')).toBeInTheDocument() | ||
await user.type(lastThought(), 'third thought') | ||
await user.keyboard('{Enter}') | ||
await act(vi.runOnlyPendingTimersAsync) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are still a few places where tests do not end in See: #2702 (comment) |
||
}) | ||
|
||
it('step subthought - how to create a thought inside thought', async () => { | ||
ensureStep(TUTORIAL_STEP_SUBTHOUGHT) | ||
expect(exportContext(store.getState(), [HOME_TOKEN], 'text/plain')).toBe( | ||
`- __ROOT__ | ||
- my first thought | ||
- my second thought | ||
- third thought | ||
- `, | ||
) | ||
raineorshine marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// Now I am going to show you how to add a thought within another thought. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This comment is redundant and can safely be removed. Likewise with similar redundant comments below. |
||
expect(screen.getByText(/Now I am going to show you how to add a thought/)).toBeInTheDocument() | ||
|
||
// since we have cursor on empty thought | ||
expect(screen.getByText(/Hit the Delete key to delete the current blank thought/)).toBeInTheDocument() | ||
expect(screen.getByText(/Then hold the Ctrl key and hit the Enter key/)).toBeInTheDocument() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's stick with a single Likewise in similar occurrences below. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @snqb Still needs attention |
||
await user.keyboard('{Backspace}') | ||
await user.keyboard('{Control>}{Enter}{/Control}') | ||
await act(vi.runOnlyPendingTimersAsync) | ||
|
||
await user.type(lastThought(), 'child') | ||
await act(vi.runOnlyPendingTimersAsync) | ||
expect(exportContext(store.getState(), [HOME_TOKEN], 'text/plain')).toBe( | ||
`- __ROOT__ | ||
- my first thought | ||
- my second thought | ||
- third thought | ||
- child`, | ||
) | ||
// as you can see, the new thought "child" is nested within "third thought" | ||
expect(screen.getByText(/As you can see, the new thought "child" is nested/)).toBeInTheDocument() | ||
expect(getByText(screen.getByTestId('tutorial-step'), /"third thought"/)).toBeInTheDocument() | ||
|
||
ensureStep(TUTORIAL_STEP_SUBTHOUGHT_ENTER) | ||
await user.click(screen.getByText('Next')) | ||
}) | ||
|
||
describe('step autoexpand', async () => { | ||
it('thoughts are automatically hidden when you click away', async () => { | ||
ensureStep(TUTORIAL_STEP_AUTOEXPAND) | ||
expect(screen.getByText(/thoughts are automatically hidden when you click away/)).toBeInTheDocument() | ||
}) | ||
|
||
it('click on "uncle" thought to hide child', async () => { | ||
await user.click(screen.getByText('my second thought')) | ||
expect(screen.getByText('Notice that "child" is hidden now.', { exact: false })).toBeInTheDocument() | ||
ensureStep(TUTORIAL_STEP_AUTOEXPAND_EXPAND) | ||
}) | ||
|
||
it('click back on a "parent" thought to reveal child', async () => { | ||
expect(screen.getByText(/Click "third thought" to reveal its subthought "child"/)).toBeInTheDocument() | ||
await user.click(screen.getAllByText('third thought').at(-1)!) | ||
expect(screen.getByText('Lovely. You have completed the tutorial', { exact: false })).toBeInTheDocument() | ||
ensureStep(TUTORIAL_STEP_SUCCESS) | ||
}) | ||
}) | ||
|
||
describe('step success - congratulates on completing first tutorial', async () => { | ||
it('congratulate on completing first tutorial', async () => { | ||
expect(screen.getByText('Lovely. You have completed the tutorial', { exact: false })).toBeInTheDocument() | ||
}) | ||
|
||
it('asks to continue with tutorial or play on own', async () => { | ||
expect(screen.getByText('Play on my own')).toBeInTheDocument() | ||
expect(screen.getByText('Learn more')).toBeInTheDocument() | ||
}) | ||
}) | ||
}) | ||
|
||
describe('Tutorial 2', async () => { | ||
beforeEach(() => createTestApp({ tutorial: true })) | ||
afterEach(cleanupTestEventHandlers) | ||
afterAll(cleanupTestApp) | ||
it('step start - tell about context menu', async () => { | ||
await user.click(screen.getByText('Learn more')) | ||
|
||
ensureStep(TUTORIAL2_STEP_START) | ||
expect(screen.getByText(`If the same thought appears in more than one place`, { exact: false })).toBeInTheDocument() | ||
expect( | ||
screen.getByText(`shows a small number to the right of the thought, for example`, { exact: false }), | ||
).toBeInTheDocument() | ||
}) | ||
|
||
it('step choose - gives 3 choices of what project to proceed with', async () => { | ||
await user.click(screen.getByText('Next')) | ||
ensureStep(TUTORIAL2_STEP_CHOOSE) | ||
expect(screen.getByText(/For this tutorial, choose what kind of content you want to create/)).toBeInTheDocument() | ||
expect(screen.getByText('To-Do List')).toBeInTheDocument() | ||
expect(screen.getByText('Journal Theme')).toBeInTheDocument() | ||
expect(screen.getByText('Book/Podcast Notes')).toBeInTheDocument() | ||
}) | ||
|
||
it('step context 1 parent - we create "Home" thought with children', async () => { | ||
await user.click(screen.getAllByText('To-Do List').at(-1)!) | ||
expect(screen.getByText(/Excellent choice. Now create a new thought with the text “Home”/)).toBeInTheDocument() | ||
ensureStep(TUTORIAL2_STEP_CONTEXT1_PARENT) | ||
|
||
// we create a `Home` thought | ||
await user.keyboard('{Enter}') | ||
await user.type(lastThought(), 'Home') | ||
await act(vi.runOnlyPendingTimersAsync) | ||
ensureStep(TUTORIAL2_STEP_CONTEXT1) | ||
|
||
// Home -> To Do | ||
expect(screen.getByText(/Let's say that you want to make a list of things/)).toBeInTheDocument() | ||
expect(screen.getByText(/Add a thought with the text "To Do"/)).toBeInTheDocument() | ||
await user.keyboard('{Control>}{Enter}{/Control}') | ||
await user.type(lastThought(), 'To Do') | ||
await act(vi.runOnlyPendingTimersAsync) | ||
ensureStep(TUTORIAL2_STEP_CONTEXT1_SUBTHOUGHT) | ||
|
||
// Home -> To Do -> or to not | ||
expect(screen.getByText(/Now add a thought to “To Do”/)).toBeInTheDocument() | ||
await user.keyboard('{Control>}{Enter}{/Control}') | ||
await user.type(lastThought(), 'or to not') | ||
await act(vi.runOnlyPendingTimersAsync) | ||
|
||
expect(exportContext(store.getState(), [HOME_TOKEN], 'text/plain')).toBe( | ||
`- __ROOT__ | ||
- Home | ||
- To Do | ||
- or to not`, | ||
) | ||
expect(screen.getByText(/Nice work!/)).toBeInTheDocument() | ||
}) | ||
|
||
it('step context 2 - we create "Work" thought with children', async () => { | ||
await user.click(screen.getByText('Next')) | ||
ensureStep(TUTORIAL2_STEP_CONTEXT2_PARENT) | ||
expect(screen.getByText(/Now we are going to create a different "To Do" list./)).toBeInTheDocument() | ||
|
||
// we created a new thought on 3rd level, so we shift-tab our way back to root | ||
await user.keyboard('{Enter}') | ||
await user.keyboard('{Shift>}{Tab}{/Shift}') | ||
await user.keyboard('{Shift>}{Tab}{/Shift}') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't mind this, but it might be more natural to select a thought at the root and create the new thought from there with one Thoughts, @raineorshine? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I agree. Shift + Tab activates the Outdent command. It would be better to stick to simple navigation and New Thought commands for completing the tutorial. That way we can avoid coupling the tests to an unrelated command. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. totally makes sense, thanks await user.click(screen.getByText('Home'))
await user.keyboard('{Enter}') |
||
await user.type(lastThought(), 'Work') | ||
await act(vi.runOnlyPendingTimersAsync) | ||
|
||
expect(screen.getByText('Now add a thought with the text "To Do"', { exact: false })).toBeInTheDocument() | ||
await user.keyboard('{Control>}{Enter}{/Control}') | ||
await user.type(lastThought(), 'To Do') | ||
|
||
expect(exportContext(store.getState(), [HOME_TOKEN], 'text/plain')).toBe( | ||
`- __ROOT__ | ||
- Home | ||
- To Do | ||
- or to not | ||
- Work | ||
- To Do`, | ||
) | ||
expect(screen.getByText('Very good!')).toBeInTheDocument() | ||
|
||
expect(screen.getAllByRole('superscript')[0]).toHaveTextContent('2') | ||
raineorshine marked this conversation as resolved.
Show resolved
Hide resolved
|
||
expect(screen.getByText('This means that “To Do” appears in two places', { exact: false })).toBeInTheDocument() | ||
|
||
expect(screen.getByText('Imagine a new work task. Add it to this “To Do” list.')) | ||
await user.keyboard('{Control>}{Enter}{/Control}') | ||
await user.type(lastThought(), 'new work task') | ||
}) | ||
|
||
it('step context view open - we press Alt+Shift+S and see "To Do" in both "Home" and "Work"', async () => { | ||
expect(exportContext(store.getState(), [HOME_TOKEN], 'text/plain')).toBe( | ||
`- __ROOT__ | ||
- Home | ||
- To Do | ||
- or to not | ||
- Work | ||
- To Do | ||
- new work task`, | ||
) | ||
await user.click(screen.getByText('Next')) | ||
await act(vi.runOnlyPendingTimersAsync) | ||
|
||
ensureStep(TUTORIAL2_STEP_CONTEXT_VIEW_SELECT) | ||
expect(screen.getByText(/First select "To Do"./)).toBeInTheDocument() | ||
await user.keyboard('{Escape}') // focus out | ||
await user.click(screen.getAllByText('To Do').at(-1)!) // and click on To Do | ||
await act(vi.runOnlyPendingTimersAsync) | ||
|
||
expect(screen.getByText("Hit Alt + Shift + S to view the current thought's contexts.")).toBeInTheDocument() | ||
await user.keyboard('{Alt>}{Shift>}S{/Shift}{/Alt}') | ||
await act(vi.runOnlyPendingTimersAsync) | ||
ensureStep(TUTORIAL2_STEP_CONTEXT_VIEW_OPEN) | ||
expect( | ||
screen.getByText( | ||
`Well, look at that. We now see all of the contexts in which "To Do" appears, namely "Home" and "Work".`, | ||
), | ||
).toBeInTheDocument() | ||
}) | ||
|
||
it('step context examples - we see real-world examples', async () => { | ||
await user.click(screen.getByText('Next')) | ||
ensureStep(TUTORIAL2_STEP_CONTEXT_VIEW_EXAMPLES) | ||
expect(screen.getByText(/Here are some real-world examples of using contexts in/)).toBeInTheDocument() | ||
expect(screen.getByText('View all thoughts related to a particular person, place, or thing.')).toBeInTheDocument() | ||
expect(screen.getByText('Keep track of quotations from different sources.')).toBeInTheDocument() | ||
}) | ||
|
||
it('step success - congratulations, hide tutorial after clicking "Finish"', async () => { | ||
await user.click(screen.getByText('Next')) | ||
expect(screen.getByText(/Congratulations! You have completed Part II of the tutorial./)).toBeInTheDocument() | ||
|
||
user.click(screen.getByText('Finish')) | ||
await act(vi.runOnlyPendingTimersAsync) | ||
|
||
expect(() => screen.getByTestId('tutorial-step')).toThrow('Unable to find an element') | ||
}) | ||
}) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,10 +13,9 @@ import storage from '../util/storage' | |
let cleanup: Await<ReturnType<typeof initialize>>['cleanup'] | ||
|
||
/** Set up testing and mock document and window functions. */ | ||
const createTestApp = async () => { | ||
const createTestApp = async ({ tutorial }: { tutorial?: boolean } = {}) => { | ||
trevinhofmann marked this conversation as resolved.
Show resolved
Hide resolved
|
||
await act(async () => { | ||
vi.useFakeTimers({ loopLimit: 100000 }) | ||
|
||
// calls initEvents, which must be manually cleaned up | ||
const init = await initialize() | ||
cleanup = init.cleanup | ||
|
@@ -33,8 +32,8 @@ const createTestApp = async () => { | |
) | ||
|
||
store.dispatch([ | ||
// skip tutorial | ||
{ type: 'tutorial', value: false }, | ||
// there are cases where we want to show tutorial on test runs, whilst mostly we don't | ||
{ type: 'tutorial', value: !!tutorial }, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FYI This is a small concession to allow point-free style in See: #2702 (comment) |
||
|
||
// close welcome modal | ||
{ type: 'closeModal' }, | ||
|
@@ -80,4 +79,13 @@ export const refreshTestApp = async () => { | |
await act(vi.runOnlyPendingTimersAsync) | ||
} | ||
|
||
/** Clear existing event listeners(e.g. keyboard, gestures), but without clearing the app. */ | ||
export const cleanupTestEventHandlers = async () => { | ||
raineorshine marked this conversation as resolved.
Show resolved
Hide resolved
|
||
await act(async () => { | ||
if (cleanup) { | ||
cleanup() | ||
} | ||
}) | ||
} | ||
|
||
export default createTestApp |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it was a bug I've found while testing