get by role region with describedby not working for react testing library - react-testing-library

I am using region role for a section in my page.
It has describedby aria label which speak of what that section does.
I am writing unit test to make sure that section exists as shown in codesandbox.
hello component:
export default ({ name }) => <h1>Hello {name}!</h1>;
test:
test("region described by hello region should exists in the dom", () => {
const { getByText, getByRole } = render(<Hello name="Jill" />);
expect(
getByRole("region", { description: "hello region" })
).toBeInTheDocument();
});

Related

Mocking authentication when testing MSAL React Apps

Our app is wrapped in the MSAL Authentication Template from #azure/msal-react in a standard way - key code segments are summarized below.
We would like to test app's individual components using react testing library (or something similar). Of course, when a React component such as SampleComponentUnderTest is to be properly rendered by a test as is shown in the simple test below, it must be wrapped in an MSAL component as well.
Is there a proper way to mock the MSAL authentication process for such purposes? Anyway to wrap a component under test in MSAL and directly provide test user's credentials to this component under test? Any references to useful documentation, blog posts, video, etc. to point us in the right direction would be greatly appreciated.
A Simple test
test('first test', () => {
const { getByText } = render(<SampleComponentUnderTest />);
const someText = getByText('A line of text');
expect(someText).toBeInTheDocument();
});
Config
export const msalConfig: Configuration = {
auth: {
clientId: `${process.env.REACT_APP_CLIENT_ID}`,
authority: `https://login.microsoftonline.com/${process.env.REACT_APP_TENANT_ID}`,
redirectUri:
process.env.NODE_ENV === 'development'
? 'http://localhost:3000/'
: process.env.REACT_APP_DEPLOY_URL,
},
cache: {
cacheLocation: 'sessionStorage',
storeAuthStateInCookie: false,
},
system: {
loggerOptions: {
loggerCallback: (level, message, containsPii) => {
if (containsPii) {
return;
}
switch (level) {
case LogLevel.Error:
console.error(message);
return;
case LogLevel.Info:
console.info(message);
return;
case LogLevel.Verbose:
console.debug(message);
return;
case LogLevel.Warning:
console.warn(message);
return;
default:
console.error(message);
}
},
},
},
};
Main app component
const msalInstance = new PublicClientApplication(msalConfig);
<MsalProvider instance={msalInstance}>
{!isAuthenticated && <UnauthenticatedHomePage />}
{isAuthenticated && <Protected />}
</MsalProvider>
Unauthenticated component
const signInClickHandler = (instance: IPublicClientApplication) => {
instance.loginRedirect(loginRequest).catch((e) => {
console.log(e);
});
};
<UnauthenticatedTemplate>
<Button onClick={() => signInClickHandler(instance)}>Sign in</Button>
</UnauthenticatedTemplate>
Protected component
<MsalAuthenticationTemplate
interactionType={InteractionType.Redirect}
errorComponent={ErrorComponent}
loadingComponent={LoadingComponent}
>
<SampleComponentUnderTest />
</MsalAuthenticationTemplate>
I had the same issue as you regarding component's test under msal-react.
It took me a couple of days to figure out how to implement a correct auth mock.
That's why I've created a package you will find here, that encapsulates all the boilerplate code : https://github.com/Mimetis/msal-react-tester
Basically, you can do multiple scenaris (user is already logged, user is not logged, user must log in etc ...) in a couple of lines, without having to configure anything and of course without having to reach Azure AD in any cases:
describe('Home page', () => {
let msalTester: MsalReactTester;
beforeEach(() => {
// new instance of msal tester for each test
msalTester = new MsalReactTester();
// spy all required msal things
msalTester.spyMsal();
});
afterEach(() => {
msalTester.resetSpyMsal();
});
test('Home page render correctly when user is logged in', async () => {
msalTester.isLogged();
render(
<MsalProvider instance={msalTester.client}>
<MemoryRouter>
<Layout>
<HomePage />
</Layout>
</MemoryRouter>
</MsalProvider>,
);
await msalTester.waitForRedirect();
let allLoggedInButtons = await screen.findAllByRole('button', { name: `${msalTester.activeAccount.name}` });
expect(allLoggedInButtons).toHaveLength(2);
});
test('Home page render correctly when user logs in using redirect', async () => {
msalTester.isNotLogged();
render(
<MsalProvider instance={msalTester.client}>
<MemoryRouter>
<Layout>
<HomePage />
</Layout>
</MemoryRouter>
</MsalProvider>,
);
await msalTester.waitForRedirect();
let signin = screen.getByRole('button', { name: 'Sign In - Redirect' });
userEvent.click(signin);
await msalTester.waitForLogin();
let allLoggedInButtons = await screen.findAllByRole('button', { name: `${msalTester.activeAccount.name}` });
expect(allLoggedInButtons).toHaveLength(2);
});
I am also curious about this, but from a slightly different perspective. I am trying to avoid littering the code base with components directly from msal in case we want to swap out identity providers at some point. The primary way to do this is to use a hook as an abstraction layer such as exposing isAuthenticated through that hook rather than the msal component library itself.
The useAuth hook would use the MSAL package directly. For the wrapper component however, I think we have to just create a separate component that either returns the MsalProvider OR a mocked auth provider of your choice. Since MsalProvider uses useContext beneath the hood I don't think you need to wrap it in another context provider.
Hope these ideas help while you are thinking through ways to do this. Know this isn't a direct answer to your question.

playwright metadata for a test suite

I am trying to add some custom metadata information in my tests like below
test.describe('my test suite',()=>{
test('my first p0 test',()=>{}).meta({
priority:0,
});
test('my first p0 test',() = {}).meta({ priority:1});
}).meta({
owningTeam: 'business-ux'
});
and use the metadata to target set of test to run.
Can you please help me with what support we have for such requirements in playwright?
You can combine playwright with allure.
installing it with
npm i -D #playwright/test allure-playwright
or you can add it into playwright.config.ts:
{
reporter: "allure-playwright";
}
see more on https://www.npmjs.com/package/allure-playwright
And also you can add test.steps and priority and test name and description and lot more meta data that it supported by allure.
I ended up following this pattern to capture additional metadata.
interface ITestMetadata {
priority: number;
owner: string;
}
const testWrapper = (metadata: ITestMetadata) => {
console.log(
`Test owner ${metadata.owner}, test Priority ${metadata.priority}`
);
return base.extend({});
};
// ----- and in test file ---
const test = testWrapper({
owner: "playwright",
priority: 0,
});
test.describe("Capture additional metadata", () => {
test("metadata being captured", () => {
expect(1).toBe(1);
});
});

React-testing-library with Ionic v5 (react) and react-hook-form- change events do not fire

I am trying to test a component rendered with Controller from react-hook-form with react-testing-library
<Controller
render={({ onChange, onBlur, value }) => (
<IonInput
onIonChange={onChange}
onIonBlur={onBlur}
value={value}
type="text"
data-testid="firstname-field"
/>
)}
name="firstName"
control={control}
defaultValue={firstName}
/>
Default values are as expected when I render the component with a some mock data. However, when I go about changing values, it seems the events are not firing. From this blog post it looks like ionic exports a set of test utils to handle ionic's custom events. After setting that up in my setupTests.ts I'm attempting to use both the ionFireEvent and the fireEvent from RTU, neither of which reflect changes in the component when I use debug(). I've set it up so I can use both fireEvent and ionFireEvent to test:
import { render, screen, wait, fireEvent } from "#testing-library/react";
import { ionFireEvent } from "#ionic/react-test-utils";
// using RTL fireEvent - no change
it("fires change event on firstname", () => {
const { baseElement } = renderGolferContext(mockGolfer);
const firstNameField = screen.getByTestId("firstname-field") as HTMLInputElement;
fireEvent.change(firstNameField, { target: { detail: { value: "Jill" } } });
expect(firstNameField.value).toBe("Jill");
});
// using IRTL ionFireEvent/ionChange - no change
it("fires change event on firstname", () => {
const { baseElement } = renderGolferContext(mockGolfer);
const firstNameField = screen.getByTestId("firstname-field") as HTMLInputElement;
ionFireEvent.ionChange(firstNameField, "Jill");
expect(firstNameField.value).toBe("Jill");
});
screen.debug(baseElement);
I've also tried moving the data-testid property to the controller rather than the IonInput suggested here, with the result being the same: no event is fired.
Here are the versions I'm using:
Using Ionic 5.1.1
#ionic/react-test-utils 0.0.3
jest 24.9
#testing-library/react 9.5
#testing-library/dom 6.16
Here is a repo I've created to demonstrate.
Any help would be much appreciated!
this line appears to be incorrect...
expect(firstNameField.value).toBe("Jill");
It should be looking at detail.value since that is what you set
expect((firstNameField as any).detail.value).toBe("Jill");
this is my test,
describe("RTL fireEvent on ion-input", () => {
it("change on firstname", () => {
const { baseElement, getByTestId } = render(<IonicHookForm />);
const firstNameField = screen.getByTestId(
"firstname-field"
) as HTMLInputElement;
fireEvent.change(firstNameField, {
target: { detail: { value: "Princess" } },
});
expect((firstNameField as any).detail.value).toEqual("Princess");
});
});

aurelia/skeleton-plugin cant run test on custum element

i have created an aurelia plugin using the skelton-plugin https://github.com/aurelia/skeleton-plugin i am now looking at writing unit tests for it.
i am stuggling to get a unit test running for a custom element ive added to the project. i started with the 'testing a custom element' example from http://aurelia.io/hub.html#/doc/article/aurelia/testing/latest/testing-components/3
template:
<template>
<div class="firstName">${firstName}</div>
</template>
vm
import {bindable} from 'aurelia-framework';
export class MyComponent {
#bindable firstName;
}
i added this to the src folder.
my test code is
import {StageComponent} from 'aurelia-testing';
import {bootstrap} from 'aurelia-bootstrapper';
describe('MyComponent', () => {
let component;
beforeEach(() => {
component = StageComponent
.withResources('my-component')
.inView('<my-component first-name.bind="firstName"></my-component>')
.boundTo({ firstName: 'Bob' });
});
it('should render first name', done => {
component.create(bootstrap).then(() => {
const nameElement = document.querySelector('.firstName');
expect(nameElement.innerHTML).toBe('Bob');
done();
}).catch(e => { console.log(e.toString()) });
});
afterEach(() => {
component.dispose();
});
});
i jspm installed aurelia-bootstrapper and aurelia-testing to get it running.
im now getting the error
Error{stack: '(SystemJS) XHR error (404 Not Found) loading http://localhost:9876/base/my-component.js
so it looks like karma cant find my component. i checked the karma.config file and the jspm loadFiles: ['test/setup.js', 'test/unit/**/*.js'], looks correct.
has any one run into a similar issue?
solved the issue.
in karma.config.js file needed to change
serveFiles: ['src//.']
to
serveFiles: ['src//*.js', 'src/**/*.html']

Best way to exeute various tests in protractor for various login id's

I have to test application using protractor for various different types of users. userid determines the type of user( Admin or partner or user). For all users i need to test the application for all major functionality. Here is what i want to do
Login User 1 > execute test1, test 2, test 3 .... Logout
Login user 2 > execute test1, test 2, test 3 .... Logout
Login User 3 > execute test1, test 2, test 3 .... Logout
Login User 4 > execute test1, test 2, test 3 .... Logout
I want to create a test framework to cover the scenario. I would appreciate inputs on best way to achieve this.
You could do this in a couple of ways. There are pro's and con's to each.
The first way is to just loop over the list of users.
describe('your suite', () => {
for (let user of users) {
describe('test for user: ' + user.username, () => {
beforeAll(() => { login(user) });
it('should do a test', () => {
// test code with user
});
it('should do another test', () => {
// test code with user
});
afterAll(() => { logout(user) });
});
}
});
Another way is to define your callback functions then compose your tests for each user.
testCallback1 = function() {
// test code with user
}
testCallback2 = function() {
// test code with user
}
describe('test for user'), () => {
let user = users[0];
beforeAll(() => { login(user) });
it('should do a test', testCallback1);
it('should do another test', testCallback2);
afterAll(() => { logout(user) });
});
describe('test for user'), () => {
let user = users[1];
beforeAll(() => { login(user) });
it('should do a test', testCallback1);
it('should do another test', testCallback2);
afterAll(() => { logout(user) });
});
There is probably a better way to do this. These are just a couple of suggestions.
I have a similar scenario and here is how I've tackled it. I added an array of users in the protractor config and then I can loop through those users to run my login tests. It's essentially the same setup as the first suggestion by cnishina in his answer. The difference for me is that my test code lives in one file and I have the code that gets the users and loops through them in another file. Probably overkill for a simple scenario but we have several different types of users (admin, non-admin, users with services, users without services, etc.) to run the login tests against so this helps to simplify that situation. I'm using TypeScript but the same thing can be done with JS if that's what you are using.
//in the protractor config
params: {
loginUsers: [
"login1#domain.test",
"login2#domain.test",
"login3#domain.test",
//and so on ...
]
}
Then I have a file where my login tests live called shared-login.tests.ts
export default class SharedLoginTests {
public static sharedSetup(url: string): void {
beforeEach(() => {
//shared setup code ...
}
afterEach(() => {
//shared tear down code ...
}
}
public static executeSharedLoginTests(username: string): void {
it(`should allow ${username} to login`, () => {
//login test code ...
}
}
}
And then I have login.tests.ts like this:
import { browser } from "protractor";
import SharedLoginTests from "./shared-login.tests";
describe(testtitle, () => {
const users: string[] = browser.params.loginUsers;
SharedLoginTests.sharedSetup(browser.params.baseUrl);
for (const user of users) {
SharedLoginTests.executeSharedLoginTests(user);
}
});