expect(...).toHaveAttribute is not a function - Why? - reactjs

I created some basic tests and followed the getting started guide on Jests website, but toHaveAttribute is not a function apparently
import React from "react";
import { fireEvent, render } from "#testing-library/react";
import userEvent from "#testing-library/user-event";
import { App } from "../App";
test("allows users to add items to their list", () => {
const { getByText, getByLabelText, getByTestId } = render(<App />);
const input = getByLabelText("What needs to be done?");
userEvent.type(getByTestId("email"), "Hello World!")
expect(getByTestId("email")).toHaveAttribute("value", "Hello, World!")
})
TypeError: expect(...).toHaveAttribute is not a function
10 | const input = getByLabelText("What needs to be done?");
11 | userEvent.type(getByTestId("email"), "Hello World!")
> 12 | expect(getByTestId("email")).toHaveAttribute("value", "Hello, World!")
| ^
13 | })
I followed the tutorial exactly so im unsure why this is happening.

The method toHaveAttribute is part of jest-dom that enables to test DOM elements. You need to verify if you have setup it properly at your project.
Install the module:
npm install --save-dev #testing-library/jest-dom
After that you can include at your jest setup file like recommended:
// In your own jest-setup.js (or any other name)
import '#testing-library/jest-dom'
// In jest.config.js add (if you haven't already)
setupFilesAfterEnv: ['<rootDir>/jest-setup.js']

I faced the same issue but easily solved it using playwright native getter - .getAttribute('someAttribute');
For example you can write something like that:
const locator = await page.locator('[name="SomeLocator"]').getAttribute('content');

I have an alternative where you use getAttribute
import { render, screen } from '#testing-library/svelte';
it('should render avatar of user', async () => {
const image = screen.getByAltText(`${MOCK_NAVBAR.username} avatar`);
expect(image.getAttribute('src')).toBe(MOCK_NAVBAR.userAvatar);
});

Related

import 'useAuthenticator' from aws-amplify outputs "TypeError: window.URL.createObjectURL is not a function" when jest testing

I would like to know how to mock this kind of dependency's error. If I omit this import and comment out related source code, the test process is going to be fine. Already have got through Mock Function Jest, but no idea what I should do.
*This project was created by CRA with template typescript.
App component.
import { useAuthenticator } from '#aws-amplify/ui-react';
const App = () => {
const { user } = useAuthenticator((context) => [context.user]);
return (
<div>{user?.username}</div>
);
};
export default App;
Test code here.
import App from 'src/App';
import { render, screen } from '#testing-library/react';
describe('First group',()=>{
test('rendering App component',()=>{
// App includes 'import { useAuthenticator } from '#aws-amplify/ui-react';'
render(<App/>)
screen.debug()
})
})
Error outputs here.
● Test suite failed to run
TypeError: window.URL.createObjectURL is not a function
> 1 | import { useAuthenticator } from '#aws-amplify/ui-react';
| ^
2 |
3 | const App = () => {
4 | const { user } = useAuthenticator((context) => [context.user]);
at define (node_modules/maplibre-gl/dist/maplibre-gl.js:25:43)
at node_modules/maplibre-gl/dist/maplibre-gl.js:35:1
at node_modules/maplibre-gl/dist/maplibre-gl.js:3:81
at Object.<anonymous> (node_modules/maplibre-gl/dist/maplibre-gl.js:6:2)
at Object.<anonymous> (node_modules/#aws-amplify/ui-react/dist/index.js:1:484)
at Object.<anonymous> (src/App.tsx:1:1)
Based on slideshowp2's answer, added setup and config file. But still hits the error. Both are located rootDir.
jest.setup.js
if (typeof window.URL.createObjectURL === 'undefined') {
window.URL.createObjectURL = jest.fn();
}
jest.config.js
module.exports = {
setupFilesAfterEnv: ['./jest.setup.js'],
};
UPDATE
This PJ was created by CRA so it required to follow their rule. I finally resolved this by slideshowp2's answer and the correct location to put the pollyfilling code.
src/setupTests.ts
if (typeof window.URL.createObjectURL === 'undefined') {
window.URL.createObjectURL = jest.fn();
}
See troubleshooting#jest
As of v2.15.0 of #aws-amplify/ui-react which included the release of Geo components, users of the Jest testing framework may run into the following error when attempting to run tests:
window.URL.createObjectURL is not a function
Please follow the steps below to resolve this issue.
Navigate to or create a Jest setup file for your project.
Add the following code to polyfill the unrecognized function in your Jest setup file:
jest.setup.js:
if (typeof window.URL.createObjectURL === 'undefined') {
window.URL.createObjectURL = jest.fn();
}
jest.config.js:
module.exports = {
//...
setupFilesAfterEnv: ['./jest.setup.js'],
//...
}
This is a known problem when using the jsdom library (a dependency of Jest) with a package that uses an unrecognized function. See this issue.

ReferenceError: window is not defined(But I used the UseEffect hook) [duplicate]

Trying to create an xterm react component in Next.js I got stuck as I'm not able to get over an error message I've never got before.
I'm trying to import a npm client-side module called xterm, but if I add the import line the application crashes.
import { Terminal } from 'xterm'
The error reads Server Error... ReferenceError: self is not defined
and then shows this chunk of code as Source
module.exports = require("xterm");
According to some research I did, this has to do with Webpack and could be helped if something like this was done:
output: {
globalObject: 'this'
}
Would you know how to fix this?
The error occurs because the library requires Web APIs to work, which are not available when Next.js pre-renders the page on the server-side.
In your case, xterm tries to access the window object which is not present on the server. To fix it, you have to dynamically import xterm so it only gets loaded on the client-side.
There are a couple of ways to achieve this in Next.js.
#1 Using dynamic import()
Move the import to your component's useEffect, then dynamically import the library and add your logic there.
useEffect(() => {
const initTerminal = async () => {
const { Terminal } = await import('xterm')
const term = new Terminal()
// Add logic with `term`
}
initTerminal()
}, [])
#2 Using next/dynamic with ssr: false
Create a component where you add the xterm logic.
// components/terminal-component
import { Terminal } from 'xterm'
function TerminalComponent() {
const term = new Terminal()
// Add logic around `term`
return <></>
}
export default TerminalComponent
Then dynamically import that component when using it.
import dynamic from 'next/dynamic'
const TerminalComponent = dynamic(() => import('<path-to>/components/terminal-component'), {
ssr: false
})
As an alternative, you could add the logic directly when dynamically importing the library with next/dynamic to avoid having an extra file for it.
import dynamic from 'next/dynamic'
const Terminal = dynamic(
{
loader: () => import('xterm').then((mod) => mod.Terminal),
render: (props, Terminal) => {
const term = new Terminal()
// Add logic with `term`
return <></>
}
},
{
ssr: false
}
)

How to test a potential null value from an async void function without using #ts-ignore in TypeScript

This is probably more of a JavaScript/TypeScript question then it is about React/Testing.
But I'll give the complete story. So I have a test app with basic routing functionality and tests to verify that the routing works.
App.tsx
https://github.com/Leejjon/pwa-seo/blob/6f621968de1184b03744a262a68d291b4571c5c1/src/App.tsx
App.test.tsx
https://github.com/Leejjon/pwa-seo/blob/6f621968de1184b03744a262a68d291b4571c5c1/src/App.test.tsx
Everything worked fine. Then I added an useEffect hook to initialize my internationalization library:
useEffect(() => {
async function initMessages() {
await intl.init({
currentLocale: "en-US",
locales
});
}
initMessages().then(() => setLoading(false));
}, [loading]);
This loads all my text assets in English. This works fine, but broke all my tests with the following error message:
Warning: An update to App inside a test was not wrapped in act(...).
After some reading up on the internet I managed to fix my tests by adding this 'act' function, here is one example:
import React from 'react';
import {act, render, fireEvent, waitForElement} from '#testing-library/react';
import "#testing-library/jest-dom/extend-expect";
import App from './App';
test('Verify home page header', async() => {
let app: HTMLElement;
await act(async () => {
const {container} = render(<App/>);
app = container;
});
// #ts-ignore
if (app) {
const pageHeaderContent = app.querySelector("#pageHeader")?.firstChild?.textContent;
expect(pageHeaderContent).toMatch('Home page');
} else {
fail("The app should have been initialized.");
}
});
Right now I'm suppressing the TS2454: Variable 'app' is used before being assigned. warning with the #ts-ignore. This is ugly. If I move my assertions into the act function, I get the same Warning: An update to App inside a test was not wrapped in act(...). error again.
Is there a way to obtain the container object destructured from the render function without having to use the #ts-ignore and the if clause to do null checking?
I created a tag for the current code related to this question:
https://github.com/Leejjon/pwa-seo/releases/tag/uglylines
Link to last commit: https://github.com/Leejjon/pwa-seo/commit/2434f78c0619be2d55f9de965149f6bd6d1a0b90
Typescript is complaining about the app variable to not have been initialised when you access it in the if-statement. You can simply fix that by assigning null to it.
let app: HTMLElement = null;
In case you use strict null checks you have to allow null on the type:
let app: HTMLElement | null = null;
After puzzling this is my result
test('Verify home page header', async() => {
let app: HTMLElement | undefined = undefined;
await act(async () => {
const {container} = render(<App/>);
app = container;
});
let appAsHtmlElement = (app as unknown as HTMLElement);
const pageHeaderContent = appAsHtmlElement.querySelector("#pageHeader")?.firstChild?.textContent;
expect(pageHeaderContent).toMatch('Home page');
});
Better suggestions (if there is some way of not having to use the 'act' function) are still welcome.

Receiving TypeError: n.getChildContext is not a function with shallowWithIntl

I started a project a few weeks ago with a react frontend and ruby backend with my coworkers. After a few code reviews, the comments about have an un-internationalized application have caught back up to us. We have an internationalized backend using an i18n gem, but were told the standard for react was to use react-intl as the frontend internationalization package. I just finished coding-up the internationalization, testing it with a couple languages to ensure it works properly. On the topic of testing, I started running into an issue which I've been banging my head against a wall with. I keep receiving this error: n.getChildContext is not a function. I'm using the package enzyme-react-intl. To test whether or not this works, I decided to only start with taking a snapshot of my components (both functional and class-based). An example of one of my tests is below along with the test suite failure I received. All of my test suites with shallowWithIntl and mountWithIntl fail. I should note that I am running my tests with the command: 'yarn jest -u'. From all of the searches and api docs I have read I'm not making any apparent mistakes, but would appreciate any help to an answer.
Here is an example test:
import React from 'react';
import { loadTranslation, shallowWithIntl } from 'enzyme-react-intl';
import Header from '../components/Header/Header';
loadTranslation("./app/javascript/translations/en-US.json");
describe('Parent header rendering', () => {
const shallowHeader = shallowWithIntl(<Header />);
it('matches the snapshot', () => {
expect(shallowHeader).toMatchSnapshot();
});
});
The Test Suite Error I receive.
FAIL app/javascript/tests/Header.test.jsx
● Parent header rendering › encountered a declaration exception
TypeError: n.getChildContext is not a function
5 |
6 | describe('Parent header rendering', () => {
> 7 | const shallowHeader = shallowWithIntl(<Header />);
| ^
8 | it('matches the snapshot', () => {
9 | expect(shallowHeader).toMatchSnapshot();
10 | });
at _enzyme (node_modules/enzyme-react-intl/lib/webpack:/enzyme-react-intl/src/index.js:47:12)
at Suite.<anonymous> (app/javascript/__tests__/Header.test.jsx:7:27)
at Object.describe (app/javascript/__tests__/Header.test.jsx:6:1)
I'm a bit of a react/jest/enzyme noob as it stands and want to learn so any pointers and critiques are greatly appreciated no matter how much they eat at my soul.
Thanks in advance!
Instead of working with the enzyme-react-intl package which is currently deprecated with the getChildContext method, reference the helper functions in the react-intl readme which are up-to-date; link to testing with enzyme. The code is written in typescript, and to change to js/jsx only a small edit was required. Code is below. Hope this helps. Don't forget to comment the source from the react-intl repo.
import React from 'react';
import {IntlProvider} from 'react-intl';
import {mount, shallow} from 'enzyme';
// You can pass your messages to the IntlProvider. Optional: remove if unneeded.
const messages = require('./translations/en-US.json'); // en-US.json
const defaultLocale = 'en-US';
const locale = defaultLocale;
export function mountWithIntl(node) {
return mount(node, {
wrappingComponent: IntlProvider,
wrappingComponentProps: {
locale,
defaultLocale,
messages,
},
});
}
export function shallowWithIntl(node) {
return shallow(node, {
wrappingComponent: IntlProvider,
wrappingComponentProps: {
locale,
defaultLocale,
messages,
},
});
}

Typescript, Jest and i18next: Can't see text in React component

I am testing a React component that uses i18next for internationalization.
The component:
import * as React from "react";
import { t } from "i18next";
export function Hello(_props) {
return <div className="widget-header">
{t("Hello, world!")}
</div>;
}
The test:
import * as React from "react";
import { render } from "enzyme";
import { Hello } from "./hello";
describe("<Hello />", () => {
it("says hi", () => {
let hi = render(<Hello />);
// FAILS!
// Expected "" to contain "Hello"
expect(hi.text()).toContain("Hello");
});
})
My suspicion is that Jest is stubbing i18next.t() to return undefined rather than "Hello, world!", but I am unsure of that.
I have tried to unmock("i18next") without any luck.
How do I write tests for i18next components in Jest?
UPDATE: I was using Typescript as my compiler. It appears that there is a hoisting issue with the loader I use for Typescript (ES6 hoists imports, jest.unmock is not- Babel users have a plugin to handle this).
Not sure why its not working but you can just mock put i18next like this:
jest.mock('i18next', () => ({
t: (i) => i
}))

Resources