Playwright Component Testing - Unable to access elements mounted in React Portal

81 Views Asked by At

In the below code formEl is mounted in normal mode and inside portal, playwright can access when its mounted normal mode, but its unable to in portal mode.

function App() {
    const [todos, setTodos] = useState<Todo[]>([{id: 1, title: "Buy Milk"}])
    const [formMode, setFormMode] = useState<"portal" | 'normal' | 'none'>('none')
    const formEl = <div className={'form'}>
        <form onSubmit={e => {
            e.preventDefault()
            setFormMode('none')
            const data = e.currentTarget.elements.todo.value;
            setTodos(prevVal => [...prevVal, {id: Date.now(), title: data}])
        }}>
            <input name={'todo'} placeholder={'Enter Todo'}/>
            <button type={'submit'}>Submit</button>
        </form>
    </div>;
    return (
        <div>
            <div className={'btn-group'}>
                <button onClick={() => setFormMode('portal')}>Add Todo via Portal</button>
                <button onClick={() => setFormMode('normal')}>Add Todo Normal Mode</button>
            </div>
            <div className={'todos'}>
                {todos.map((todo) => (<div className={'todo-item'} key={todo.id}>
                    {todo.title}
                </div>))}
            </div>
            {formMode === 'portal' && createPortal(
                formEl,
                document.body
            )}
            {formMode === 'normal' && formEl}
        </div>
    )
}

Test Code


test('normal mode', async ({ mount }) => {
  const component = await mount(<App />);
  await component.getByRole('button', { name: 'Add Todo Normal Mode' }).click();
  await expect(component.getByPlaceholder('Enter Todo')).toBeVisible()
  await component.getByPlaceholder('Enter Todo').fill("Learn Playwright")
  await component.getByRole('button', {name: 'submit'}).click()
  await expect(component.getByText("Learn Playwright")).toBeVisible()
});

test('portal mode', async ({ mount }) => {
  const component = await mount(<App />);
  await component.getByRole('button', { name: 'Add Todo via Portal' }).click();
  await expect(component.getByPlaceholder('Enter Todo')).toBeVisible()
  await component.getByPlaceholder('Enter Todo').fill("Learn Playwright")
  await component.getByRole('button', {name: 'submit'}).click()
  await expect(component.getByText("Learn Playwright")).toBeVisible()
});
1

There are 1 best solutions below

0
Nitesh Baghel On

You can use the page.evaluteHandle method to get the element handle from inside the browser context:

const inputHandle = await page.evaluteHandle(()=>{
 return document.querySelector('input[placeholder="Enter Todo"]')
})

const input = await inputHandle.asElement()