Skip to content

Commit

Permalink
Add component tests for SideNav
Browse files Browse the repository at this point in the history
Add component test for `SideNavMobile` and `SideNavMenu`

Partially resolves: Add E2E and component tests #33
  • Loading branch information
GodHermit committed Jul 25, 2023
1 parent 5c5fb85 commit dda2f43
Show file tree
Hide file tree
Showing 4 changed files with 453 additions and 40 deletions.
40 changes: 0 additions & 40 deletions cypress/support/component.ts

This file was deleted.

69 changes: 69 additions & 0 deletions cypress/support/component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// ***********************************************************
// This example support/component.ts is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************

// Import commands.js using ES2015 syntax:
import './commands';

// Alternatively you can use CommonJS syntax:
// require('./commands')

import store from '@/_store';
import _theme from '@/_theme';
import { customStorageManager } from '@/_theme/customStorageManager';
import { CacheProvider } from '@chakra-ui/next-js';
import { ChakraProvider } from '@chakra-ui/react';
import { MountOptions, MountReturn, mount } from 'cypress/react18';
import { NextIntlClientProvider } from 'next-intl';
import { Provider } from 'react-redux';

// Augment the Cypress namespace to include type definitions for
// your custom command.
// Alternatively, can be defined in cypress/support/component.d.ts
// with a <reference path="./component" /> at the top of your spec.
declare global {
namespace Cypress {
interface Chainable {
mountWithProviders(
component: React.ReactNode,
options?: MountOptions & {
reduxStore?: typeof store,
locale?: string,
}
): Cypress.Chainable<MountReturn>
}
}
}

Cypress.Commands.add('mountWithProviders', (component, options = {}) => {
// Use the default store if one is not provided
const { reduxStore = store, locale = 'en', ...mountOptions } = options;

const wrapped = (
<NextIntlClientProvider locale={locale}>
<Provider store={store} /*serverState={preloadedState}*/>
<CacheProvider>
<ChakraProvider theme={_theme} colorModeManager={customStorageManager}>
{component}
</ChakraProvider>
</CacheProvider>
</Provider>
</NextIntlClientProvider>
);

return mount(wrapped, mountOptions);
});

// Example use:
// cy.mount(<MyComponent />)
282 changes: 282 additions & 0 deletions src/components/SideNav/SideNav.cy.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,282 @@
import { groupBy } from '@/_helpers/groupBy';
import store from '@/_store';
import { setArticlesState } from '@/_store/slices/articlesSlice';
import { theme } from '@chakra-ui/react';
import SideNav from './index';

function convertEmToPixels(em: number) {
return em * parseFloat(getComputedStyle(document.documentElement).fontSize);
}

function normalizeSlug(slug: string, locale: string = 'en') {
return slug === '/' ? `/${locale}` : `/${locale}${slug}`;
}

const articlesMetadata = [{
name: 'With icon',
icon: 'MdInfo',
slug: '/'
}, {
name: 'Without icon',
icon: '',
slug: '/without-icon',
}, {
name: 'In group (with icon)',
icon: 'MdCategory',
slug: '/group',
groupName: 'Group 1'
}, {
name: 'In group (without icon)',
icon: '',
slug: '/',
groupName: 'Group 1'
}];

describe('<SideNav />', () => {

context('Breakpoint: md', () => {
beforeEach(() => {
store.dispatch(setArticlesState({ // Restore default state
isLoading: true
}));

cy.viewport(convertEmToPixels(parseFloat(theme.breakpoints.md)), 500);
cy.mountWithProviders(<SideNav />, { reduxStore: store, locale: 'en' });
});

it('Should show loading animation', () => {
expect(store.getState().articles.isLoading).to.be.true;

cy
.get('.chakra-spinner')
.should('contain', 'Loading')
});

it('Should show error message', () => {
store.dispatch(setArticlesState({
articlesMetadata: [],
isLoading: false,
}));

cy
.findByText('loadingError') // Text of message in i18n file
.should('be.visible'); // Error message

cy
.get('button')
.findByText('tryAgain') // Text of message in i18n file
.should('be.visible'); // Button to try again
});

it('Should show list of articles\' icons', () => {
store.dispatch(setArticlesState({
articlesMetadata,
isLoading: false
}));

const expectedList = groupBy(articlesMetadata, article => article.groupName);

expectedList.forEach((articles, groupName) => {
if (groupName) { // If groupName is not null
cy
.findByText(groupName)
.should('not.exist'); // Group name
}

articles.forEach(article => { // Add articles as menu items
cy.log('**Assert icon**');
cy
.get(`[aria-label="${article.name}"]`)
.should('be.visible') // Check if icon is visible
.and(
'have.text',
// Check if icon is empty or not
article.icon.length > 0 ? '' : article.name[0] // If icon is empty, use first letter of article name
)
.and(
'have.attr',
'href',
normalizeSlug(article.slug)
);

cy.log('**Assert tooltip**');
cy
.get(`[aria-label="${article.name}"]`)
.focus();

cy.wait(300); // Wait for tooltip to appear

cy
.findByText(article.name)
.scrollIntoView()
.should('be.visible'); // Tooltip
});
});
});
});

context('Breakpoint: lg', () => {
beforeEach(() => {
store.dispatch(setArticlesState({ // Restore default state
isLoading: true
}));

cy.viewport(convertEmToPixels(parseFloat(theme.breakpoints.lg)), 500);
cy.mountWithProviders(<SideNav />, { reduxStore: store, locale: 'en' });
});

it('Should show loading animation', () => {
expect(store.getState().articles.isLoading).to.be.true;

cy
.get('.chakra-spinner')
.should('contain', 'Loading')
});

it('Should show error message', () => {
store.dispatch(setArticlesState({
articlesMetadata: [],
isLoading: false,
}));

cy
.findByText('loadingError') // Text of message in i18n file
.should('be.visible'); // Error message

cy
.get('button')
.findByText('tryAgain') // Text of message in i18n file
.should('be.visible'); // Button to try again
});

it('Should show list of articles', () => {
store.dispatch(setArticlesState({
articlesMetadata,
isLoading: false
}));

const expectedList = groupBy(articlesMetadata, article => article.groupName);

expectedList.forEach((articles, groupName) => {
if (groupName) { // If groupName is not null
cy
.findByText(groupName)
.should('be.visible'); // Group name
}

articles.forEach(article => { // Add articles as menu items
cy
.findByText(article.name)
.should('be.visible')
.and(
'have.attr',
'href',
normalizeSlug(article.slug)
); // Article name
});
});
});
});

context('Breakpoint change: md -> lg', () => {
beforeEach(() => {
store.dispatch(setArticlesState({ // Restore default state
isLoading: true
}));

cy.viewport(convertEmToPixels(parseFloat(theme.breakpoints.md)), 500);
cy.mountWithProviders(<SideNav />, { reduxStore: store, locale: 'en' });
});

it('Should show articles\' names', () => {
store.dispatch(setArticlesState({
articlesMetadata,
isLoading: false
}));

const expectedList = groupBy(articlesMetadata, article => article.groupName);

expectedList.forEach((articles, groupName) => {
if (groupName) { // If groupName is not null
cy
.findByText(groupName)
.should('not.exist'); // Group name
}

articles.forEach(article => { // Add articles as menu items
cy
.get(`[aria-label="${article.name}"]`)
.should('be.visible')
.and('not.have.text', article.name);
});
});

cy.viewport(convertEmToPixels(parseFloat(theme.breakpoints.lg)), 500);

expectedList.forEach((articles, groupName) => {
if (groupName) { // If groupName is not null
cy
.findByText(groupName)
.should('be.visible'); // Group name
}

articles.forEach(article => { // Add articles as menu items
cy
.findByText(article.name)
.should('be.visible');
});
});
});
});

context('Breakpoint change: lg -> md', () => {
beforeEach(() => {
store.dispatch(setArticlesState({ // Restore default state
isLoading: true
}));

cy.viewport(convertEmToPixels(parseFloat(theme.breakpoints.lg)), 500);
cy.mountWithProviders(<SideNav />, { reduxStore: store, locale: 'en' });
});

it('Should show articles\' names', () => {
store.dispatch(setArticlesState({
articlesMetadata,
isLoading: false
}));

const expectedList = groupBy(articlesMetadata, article => article.groupName);

expectedList.forEach((articles, groupName) => {
if (groupName) { // If groupName is not null
cy
.findByText(groupName)
.should('be.visible'); // Group name
}

articles.forEach(article => { // Add articles as menu items
cy
.findByText(article.name)
.should('be.visible');
});
});

cy.viewport(convertEmToPixels(parseFloat(theme.breakpoints.md)), 500);

expectedList.forEach((articles, groupName) => {
if (groupName) { // If groupName is not null
cy
.findByText(groupName)
.should('not.exist'); // Group name
}

articles.forEach(article => { // Add articles as menu items
cy
.get(`[aria-label="${article.name}"]`)
.should('be.visible')
.and('not.have.text', article.name);
});
});
});
});
})
Loading

0 comments on commit dda2f43

Please sign in to comment.