mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2025-12-19 18:18:27 -05:00
refactor: client jest -> vitest (#62177)
This commit is contained in:
committed by
GitHub
parent
c7354cff89
commit
881dfd8f78
33
client/__mocks__/gatsby.ts
Normal file
33
client/__mocks__/gatsby.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import React from 'react';
|
||||
import type { GatsbyLinkProps } from 'gatsby';
|
||||
import { vi } from 'vitest';
|
||||
import gatsby from 'gatsby';
|
||||
|
||||
import envData from '../config/env.json';
|
||||
const { clientLocale } = envData;
|
||||
|
||||
export const navigate = vi.fn();
|
||||
export const graphql = vi.fn();
|
||||
export const Link = vi
|
||||
.fn()
|
||||
.mockImplementation(({ to, ...rest }: GatsbyLinkProps<undefined | boolean>) =>
|
||||
React.createElement('a', { ...rest, href: to })
|
||||
);
|
||||
export const withPrefix = vi.fn().mockImplementation((path: string) => {
|
||||
const pathPrefix = clientLocale === 'english' ? '' : '/' + clientLocale;
|
||||
return pathPrefix + path;
|
||||
});
|
||||
export const StaticQuery = vi.fn();
|
||||
export const useStaticQuery = vi.fn();
|
||||
|
||||
export default {
|
||||
// ...existing code...
|
||||
// spread the actual gatsby module to keep other exports working
|
||||
...gatsby,
|
||||
navigate,
|
||||
graphql,
|
||||
Link,
|
||||
withPrefix,
|
||||
StaticQuery,
|
||||
useStaticQuery
|
||||
};
|
||||
@@ -1,7 +1,5 @@
|
||||
import React from 'react';
|
||||
|
||||
const reactI18next = jest.genMockFromModule('react-i18next');
|
||||
|
||||
// modified from https://github.com/i18next/react-i18next/blob/master/example/test-jest/src/__mocks__/react-i18next.js
|
||||
const hasChildren = node =>
|
||||
node && (node.children || (node.props && node.props.children));
|
||||
@@ -58,9 +56,4 @@ const Trans = ({ children }) =>
|
||||
<Component t={() => ''} {...props} />
|
||||
); */
|
||||
|
||||
// reactI18next.translate = translate;
|
||||
reactI18next.withTranslation = withTranslation;
|
||||
reactI18next.useTranslation = useTranslation;
|
||||
reactI18next.Trans = Trans;
|
||||
|
||||
module.exports = reactI18next;
|
||||
module.exports = { withTranslation, useTranslation, Trans };
|
||||
4
client/__mocks__/react-spinkit.tsx
Normal file
4
client/__mocks__/react-spinkit.tsx
Normal file
@@ -0,0 +1,4 @@
|
||||
import React from 'react';
|
||||
|
||||
// eslint-disable-next-line react/display-name
|
||||
export default () => <div>Spinner</div>;
|
||||
@@ -1,5 +1,9 @@
|
||||
import fs from 'fs';
|
||||
import { setup } from 'jest-json-schema-extended';
|
||||
import { dirname } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
import { describe, test, expect } from 'vitest';
|
||||
|
||||
import { availableLangs, LangNames, LangCodes } from '../../shared/config/i18n';
|
||||
import {
|
||||
catalogSuperBlocks,
|
||||
@@ -7,8 +11,6 @@ import {
|
||||
} from '../../shared/config/curriculum';
|
||||
import intro from './locales/english/intro.json';
|
||||
|
||||
setup();
|
||||
|
||||
interface Intro {
|
||||
[key: string]: {
|
||||
title: string;
|
||||
@@ -41,6 +43,9 @@ const filesThatShouldExist = [
|
||||
}
|
||||
];
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
|
||||
const path = `${__dirname}/locales`;
|
||||
|
||||
describe('Locale tests:', () => {
|
||||
@@ -77,19 +82,25 @@ describe('Intro file structure tests:', () => {
|
||||
const typedIntro = intro as unknown as Intro;
|
||||
const superblocks = Object.values(SuperBlocks);
|
||||
for (const superBlock of superblocks) {
|
||||
expect(typeof typedIntro[superBlock].title).toBe('string');
|
||||
test(`superBlock ${superBlock} has required properties`, () => {
|
||||
expect(typeof typedIntro[superBlock].title).toBe('string');
|
||||
|
||||
// catalog superblocks should have a summary
|
||||
if (catalogSuperBlocks.includes(superBlock)) {
|
||||
expect(typedIntro[superBlock].intro).toBeInstanceOf(Array);
|
||||
}
|
||||
|
||||
// catalog superblocks should have a summary
|
||||
if (catalogSuperBlocks.includes(superBlock)) {
|
||||
expect(typedIntro[superBlock].intro).toBeInstanceOf(Array);
|
||||
}
|
||||
|
||||
expect(typedIntro[superBlock].intro).toBeInstanceOf(Array);
|
||||
expect(typedIntro[superBlock].blocks).toBeInstanceOf(Object);
|
||||
const blocks = Object.keys(typedIntro[superBlock].blocks);
|
||||
blocks.forEach(block => {
|
||||
expect(typeof typedIntro[superBlock].blocks[block].title).toBe('string');
|
||||
expect(typedIntro[superBlock].blocks[block].intro).toBeInstanceOf(Array);
|
||||
expect(typedIntro[superBlock].blocks).toBeInstanceOf(Object);
|
||||
const blocks = Object.keys(typedIntro[superBlock].blocks);
|
||||
blocks.forEach(block => {
|
||||
expect(typeof typedIntro[superBlock].blocks[block].title).toBe(
|
||||
'string'
|
||||
);
|
||||
expect(typedIntro[superBlock].blocks[block].intro).toBeInstanceOf(
|
||||
Array
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
"serve-ci": "serve -l 8000 -c serve.json public",
|
||||
"prestand-alone": "pnpm run prebuild",
|
||||
"stand-alone": "gatsby develop",
|
||||
"test": "vitest",
|
||||
"validate-keys": "tsx --tsconfig ../tsconfig.json ../tools/scripts/lint/validate-keys"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -132,7 +133,9 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/plugin-syntax-dynamic-import": "7.8.3",
|
||||
"@testing-library/jest-dom": "^6.8.0",
|
||||
"@testing-library/react": "12.1.5",
|
||||
"@testing-library/react-hooks": "^8.0.1",
|
||||
"@total-typescript/ts-reset": "^0.5.0",
|
||||
"@types/canvas-confetti": "^1.6.0",
|
||||
"@types/gatsbyjs__reach-router": "1.3.0",
|
||||
@@ -156,6 +159,7 @@
|
||||
"@types/sanitize-html": "^2.8.0",
|
||||
"@types/store": "^2.0.2",
|
||||
"@types/validator": "^13.7.12",
|
||||
"@vitest/ui": "^3.2.4",
|
||||
"autoprefixer": "10.4.17",
|
||||
"babel-plugin-macros": "3.1.0",
|
||||
"core-js": "2.6.12",
|
||||
@@ -170,6 +174,7 @@
|
||||
"react-test-renderer": "17.0.2",
|
||||
"redux-saga-test-plan": "4.0.6",
|
||||
"serve": "13.0.4",
|
||||
"vitest": "^3.2.4",
|
||||
"webpack": "5.90.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,188 +0,0 @@
|
||||
interface MockChallengeNodes {
|
||||
challenge: {
|
||||
fields: {
|
||||
slug: string;
|
||||
blockName: string;
|
||||
};
|
||||
id: string;
|
||||
block: string;
|
||||
title: string;
|
||||
superBlock: string;
|
||||
dashedName: string;
|
||||
};
|
||||
}
|
||||
|
||||
const mockChallengeNodes: MockChallengeNodes[] = [
|
||||
{
|
||||
challenge: {
|
||||
fields: {
|
||||
slug: '/super-block-one/block-a/challenge-one',
|
||||
blockName: 'Block A'
|
||||
},
|
||||
id: 'a',
|
||||
block: 'block-a',
|
||||
title: 'Challenge One',
|
||||
superBlock: 'super-block-one',
|
||||
dashedName: 'challenge-one'
|
||||
}
|
||||
},
|
||||
{
|
||||
challenge: {
|
||||
fields: {
|
||||
slug: '/super-block-one/block-a/challenge-two',
|
||||
blockName: 'Block A'
|
||||
},
|
||||
id: 'b',
|
||||
block: 'block-a',
|
||||
title: 'Challenge Two',
|
||||
superBlock: 'super-block-one',
|
||||
dashedName: 'challenge-two'
|
||||
}
|
||||
},
|
||||
{
|
||||
challenge: {
|
||||
fields: {
|
||||
slug: '/super-block-one/block-b/challenge-one',
|
||||
blockName: 'Block B'
|
||||
},
|
||||
id: 'c',
|
||||
block: 'block-b',
|
||||
title: 'Challenge One',
|
||||
superBlock: 'super-block-one',
|
||||
dashedName: 'challenge-one'
|
||||
}
|
||||
},
|
||||
{
|
||||
challenge: {
|
||||
fields: {
|
||||
slug: '/super-block-one/block-b/challenge-two',
|
||||
blockName: 'Block B'
|
||||
},
|
||||
|
||||
id: 'd',
|
||||
block: 'block-b',
|
||||
title: 'Challenge Two',
|
||||
superBlock: 'super-block-one',
|
||||
dashedName: 'challenge-two'
|
||||
}
|
||||
},
|
||||
{
|
||||
challenge: {
|
||||
fields: {
|
||||
slug: '/super-block-one/block-c/challenge-one',
|
||||
blockName: 'Block C'
|
||||
},
|
||||
id: 'e',
|
||||
block: 'block-c',
|
||||
title: 'Challenge One',
|
||||
superBlock: 'super-block-one',
|
||||
dashedName: 'challenge-one'
|
||||
}
|
||||
},
|
||||
{
|
||||
challenge: {
|
||||
fields: {
|
||||
slug: '/super-block-two/block-a/challenge-one',
|
||||
blockName: 'Block A'
|
||||
},
|
||||
id: 'f',
|
||||
block: 'block-a',
|
||||
title: 'Challenge One',
|
||||
superBlock: 'super-block-two',
|
||||
dashedName: 'challenge-one'
|
||||
}
|
||||
},
|
||||
{
|
||||
challenge: {
|
||||
fields: {
|
||||
slug: '/super-block-two/block-a/challenge-two',
|
||||
blockName: 'Block A'
|
||||
},
|
||||
id: 'g',
|
||||
block: 'block-a',
|
||||
title: 'Challenge Two',
|
||||
superBlock: 'super-block-two',
|
||||
dashedName: 'challenge-two'
|
||||
}
|
||||
},
|
||||
{
|
||||
challenge: {
|
||||
fields: {
|
||||
slug: '/super-block-two/block-b/challenge-one',
|
||||
blockName: 'Block B'
|
||||
},
|
||||
id: 'h',
|
||||
block: 'block-b',
|
||||
title: 'Challenge One',
|
||||
superBlock: 'super-block-two',
|
||||
dashedName: 'challenge-one'
|
||||
}
|
||||
},
|
||||
{
|
||||
challenge: {
|
||||
fields: {
|
||||
slug: '/super-block-two/block-b/challenge-two',
|
||||
blockName: 'Block B'
|
||||
},
|
||||
id: 'i',
|
||||
block: 'block-b',
|
||||
title: 'Challenge Two',
|
||||
superBlock: 'super-block-two',
|
||||
dashedName: 'challenge-two'
|
||||
}
|
||||
},
|
||||
{
|
||||
challenge: {
|
||||
fields: {
|
||||
slug: '/super-block-three/block-a/challenge-one',
|
||||
blockName: 'Block A'
|
||||
},
|
||||
id: 'j',
|
||||
block: 'block-a',
|
||||
title: 'Challenge One',
|
||||
superBlock: 'super-block-three',
|
||||
dashedName: 'challenge-one'
|
||||
}
|
||||
},
|
||||
{
|
||||
challenge: {
|
||||
fields: {
|
||||
slug: '/super-block-three/block-c/challenge-two',
|
||||
blockName: 'Block C'
|
||||
},
|
||||
id: 'k',
|
||||
block: 'block-c',
|
||||
title: 'Challenge Two',
|
||||
superBlock: 'super-block-three',
|
||||
dashedName: 'challenge-two'
|
||||
}
|
||||
},
|
||||
{
|
||||
challenge: {
|
||||
fields: {
|
||||
slug: '/super-block-three/block-c/challenge-three',
|
||||
blockName: 'Block C'
|
||||
},
|
||||
id: 'l',
|
||||
block: 'block-c',
|
||||
title: 'Challenge Three',
|
||||
superBlock: 'super-block-three',
|
||||
dashedName: 'challenge-three'
|
||||
}
|
||||
},
|
||||
{
|
||||
challenge: {
|
||||
fields: {
|
||||
slug: '/super-block-four/block-a/challenge-one',
|
||||
blockName: 'Block A'
|
||||
},
|
||||
id: 'm',
|
||||
block: 'block-a',
|
||||
title: 'Challenge One',
|
||||
superBlock: 'super-block-four',
|
||||
dashedName: 'challenge-one'
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
export default mockChallengeNodes;
|
||||
@@ -1 +0,0 @@
|
||||
export default {};
|
||||
@@ -1,27 +0,0 @@
|
||||
import React from 'react';
|
||||
import { GatsbyLinkProps } from 'gatsby';
|
||||
const gatsby: NodeModule = jest.requireActual('gatsby');
|
||||
|
||||
import envData from '../../config/env.json';
|
||||
|
||||
const { clientLocale } = envData;
|
||||
|
||||
module.exports = {
|
||||
...gatsby,
|
||||
navigate: jest.fn(),
|
||||
graphql: jest.fn(),
|
||||
Link: jest.fn().mockImplementation(
|
||||
// these props are invalid for an `a` tag
|
||||
({ to, ...rest }: GatsbyLinkProps<undefined | boolean>) =>
|
||||
React.createElement('a', {
|
||||
...rest,
|
||||
href: to
|
||||
})
|
||||
),
|
||||
withPrefix: jest.fn().mockImplementation((path: string) => {
|
||||
const pathPrefix = clientLocale === 'english' ? '' : '/' + clientLocale;
|
||||
return pathPrefix + path;
|
||||
}),
|
||||
StaticQuery: jest.fn(),
|
||||
useStaticQuery: jest.fn()
|
||||
};
|
||||
@@ -1 +0,0 @@
|
||||
export default {};
|
||||
@@ -1,3 +1,4 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import {
|
||||
wrapHandledError,
|
||||
unwrapHandledError
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
import callGA, { GAevent } from './call-ga';
|
||||
import TagManager from '.';
|
||||
|
||||
jest.mock('.', () => ({
|
||||
dataLayer: jest.fn()
|
||||
}));
|
||||
|
||||
describe('callGA function', () => {
|
||||
it('calls TagManager dataLayer with the same arguments', () => {
|
||||
const eventDataMock: GAevent = {
|
||||
event: 'donation',
|
||||
action: 'Donate Page Stripe Payment Submission',
|
||||
duration: 'month',
|
||||
amount: 500,
|
||||
completed_challenges: 100,
|
||||
completed_challenges_session: 10,
|
||||
isSignedIn: true
|
||||
};
|
||||
callGA(eventDataMock);
|
||||
expect(TagManager.dataLayer).toHaveBeenCalledWith({
|
||||
dataLayer: eventDataMock
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -2,13 +2,14 @@
|
||||
// @ts-nocheck Likely need to not use ShallowRenderer
|
||||
import React from 'react';
|
||||
import ShallowRenderer from 'react-test-renderer/shallow';
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import envData from '../../config/env.json';
|
||||
|
||||
import { ShowSettings } from './show-settings';
|
||||
|
||||
const { apiLocation } = envData as Record<string, string>;
|
||||
|
||||
jest.mock('../analytics');
|
||||
vi.mock('../analytics');
|
||||
|
||||
describe('<ShowSettings />', () => {
|
||||
it('renders to the DOM when user is logged in', () => {
|
||||
@@ -34,24 +35,24 @@ describe('<ShowSettings />', () => {
|
||||
});
|
||||
});
|
||||
|
||||
const navigate = jest.fn();
|
||||
const navigate = vi.fn();
|
||||
const loggedInProps = {
|
||||
createFlashMessage: jest.fn(),
|
||||
hardGoTo: jest.fn(),
|
||||
createFlashMessage: vi.fn(),
|
||||
hardGoTo: vi.fn(),
|
||||
isSignedIn: true,
|
||||
navigate: navigate,
|
||||
showLoading: false,
|
||||
submitNewAbout: jest.fn(),
|
||||
toggleTheme: jest.fn(),
|
||||
updateSocials: jest.fn(),
|
||||
updateIsHonest: jest.fn(),
|
||||
updatePortfolio: jest.fn(),
|
||||
updateQuincyEmail: jest.fn(),
|
||||
submitNewAbout: vi.fn(),
|
||||
toggleTheme: vi.fn(),
|
||||
updateSocials: vi.fn(),
|
||||
updateIsHonest: vi.fn(),
|
||||
updatePortfolio: vi.fn(),
|
||||
updateQuincyEmail: vi.fn(),
|
||||
user: {
|
||||
about: '',
|
||||
completedChallenges: []
|
||||
},
|
||||
verifyCert: jest.fn()
|
||||
verifyCert: vi.fn()
|
||||
};
|
||||
const loggedOutProps = { ...loggedInProps };
|
||||
loggedOutProps.isSignedIn = false;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`<Footer /> matches snapshot 1`] = `
|
||||
exports[`<Footer /> > matches snapshot 1`] = `
|
||||
<footer
|
||||
className="site-footer"
|
||||
>
|
||||
@@ -342,7 +342,7 @@ exports[`<Footer /> matches snapshot 1`] = `
|
||||
<img
|
||||
alt="Download on the App Store"
|
||||
lang="en"
|
||||
src={{}}
|
||||
src="/src/assets/images/footer-ads/apple-store-badge.svg"
|
||||
/>
|
||||
</a>
|
||||
</li>
|
||||
@@ -355,7 +355,7 @@ exports[`<Footer /> matches snapshot 1`] = `
|
||||
<img
|
||||
alt="Get it on Google Play"
|
||||
lang="en"
|
||||
src={{}}
|
||||
src="/src/assets/images/footer-ads/google-play-badge.svg"
|
||||
/>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
import renderer from 'react-test-renderer';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import Footer from '.';
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
import { create, ReactTestRendererJSON } from 'react-test-renderer';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import AuthOrProfile from './components/auth-or-profile';
|
||||
|
||||
const defaultUserProps = {
|
||||
@@ -37,7 +38,7 @@ const topDonatingContributorUserProps = {
|
||||
}
|
||||
};
|
||||
|
||||
jest.mock('../../analytics');
|
||||
vi.mock('../../analytics');
|
||||
|
||||
describe('<AuthOrProfile />', () => {
|
||||
it('has avatar with default border for default users', () => {
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { createStore } from '../../redux/create-store';
|
||||
|
||||
import Intro from '.';
|
||||
|
||||
jest.mock('../../analytics');
|
||||
vi.mock('../../analytics');
|
||||
vi.mock('../../utils/get-words');
|
||||
|
||||
function renderWithRedux(
|
||||
ui: JSX.Element,
|
||||
@@ -45,12 +47,12 @@ const loggedInProps = {
|
||||
completedChallengeCount: 0,
|
||||
isSignedIn: true,
|
||||
name: 'Development User',
|
||||
navigate: () => jest.fn(),
|
||||
navigate: () => vi.fn(),
|
||||
pending: false,
|
||||
slug: '/',
|
||||
username: 'DevelopmentUser',
|
||||
isDonating: false,
|
||||
onLearnDonationAlertClick: () => jest.fn()
|
||||
onLearnDonationAlertClick: () => vi.fn()
|
||||
};
|
||||
|
||||
const loggedOutProps = {
|
||||
@@ -58,10 +60,10 @@ const loggedOutProps = {
|
||||
completedChallengeCount: 0,
|
||||
isSignedIn: false,
|
||||
name: '',
|
||||
navigate: () => jest.fn(),
|
||||
navigate: () => vi.fn(),
|
||||
pending: false,
|
||||
slug: '/',
|
||||
username: '',
|
||||
isDonating: false,
|
||||
onLearnDonationAlertClick: () => jest.fn()
|
||||
onLearnDonationAlertClick: () => vi.fn()
|
||||
};
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
import React from 'react';
|
||||
import { render, act, screen } from '@testing-library/react';
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
|
||||
import OfflineWarning from './offline-warning';
|
||||
|
||||
jest.useFakeTimers();
|
||||
vi.useFakeTimers();
|
||||
|
||||
describe('<OfflineWarning />', () => {
|
||||
it('renders null when isOnline, isServerOnline and isSignedIn are true', () => {
|
||||
const { container } = render(
|
||||
<OfflineWarning isOnline={true} isServerOnline={true} isSignedIn={true} />
|
||||
);
|
||||
act(() => jest.runAllTimers());
|
||||
act(() => void vi.runAllTimers());
|
||||
expect(container).toBeEmptyDOMElement();
|
||||
});
|
||||
|
||||
@@ -22,7 +23,7 @@ describe('<OfflineWarning />', () => {
|
||||
isSignedIn={true}
|
||||
/>
|
||||
);
|
||||
act(() => jest.runAllTimers());
|
||||
act(() => void vi.runAllTimers());
|
||||
expect(screen.getByText('misc.offline')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -34,7 +35,7 @@ describe('<OfflineWarning />', () => {
|
||||
isSignedIn={true}
|
||||
/>
|
||||
);
|
||||
act(() => jest.runAllTimers());
|
||||
act(() => void vi.runAllTimers());
|
||||
expect(screen.getByText('placeholder').tagName).toBe('A');
|
||||
expect(screen.getByText('placeholder')).toHaveAttribute(
|
||||
'href',
|
||||
@@ -50,7 +51,7 @@ describe('<OfflineWarning />', () => {
|
||||
isSignedIn={false}
|
||||
/>
|
||||
);
|
||||
act(() => jest.runAllTimers());
|
||||
act(() => void vi.runAllTimers());
|
||||
expect(container).toBeEmptyDOMElement();
|
||||
});
|
||||
|
||||
@@ -62,7 +63,7 @@ describe('<OfflineWarning />', () => {
|
||||
isSignedIn={true}
|
||||
/>
|
||||
);
|
||||
act(() => jest.runAllTimers());
|
||||
act(() => void vi.runAllTimers());
|
||||
expect(screen.getByText('misc.offline')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,13 +2,15 @@ import { render, waitFor } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { I18nextProvider } from 'react-i18next';
|
||||
import { Provider } from 'react-redux';
|
||||
import { describe, vi, test, expect } from 'vitest';
|
||||
|
||||
import { i18nextCodes } from '../../../shared/config/i18n';
|
||||
import i18nTestConfig from '../../i18n/config-for-tests';
|
||||
import { createStore } from '../redux/create-store';
|
||||
import AppMountNotifier from './app-mount-notifier';
|
||||
|
||||
jest.unmock('react-i18next');
|
||||
vi.unmock('react-i18next');
|
||||
vi.mock('../utils/get-words');
|
||||
|
||||
type Language = keyof typeof i18nextCodes;
|
||||
type LanguagePair = [string, string];
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// @vitest-environment jsdom
|
||||
import { beforeEach, describe, expect, it } from 'vitest';
|
||||
import createLanguageRedirect from './create-language-redirect';
|
||||
|
||||
describe('createLanguageRedirect for clientLocale === english', () => {
|
||||
@@ -17,8 +19,6 @@ describe('createLanguageRedirect for clientLocale === english', () => {
|
||||
const dothrakiPageURL =
|
||||
'https://www.freecodecamp.org/dothraki/learn/responsive-web-design/basic-html-and-html5/inform-with-the-paragraph-element';
|
||||
|
||||
const originalLocation = window.location;
|
||||
|
||||
beforeEach(() => {
|
||||
Object.defineProperty(window, 'location', {
|
||||
writable: true,
|
||||
@@ -26,10 +26,6 @@ describe('createLanguageRedirect for clientLocale === english', () => {
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
window.location = originalLocation;
|
||||
});
|
||||
|
||||
[
|
||||
{ lang: 'english', url: currentPageURL },
|
||||
{ lang: 'chinese', url: chinesePageURL },
|
||||
@@ -60,8 +56,6 @@ describe('createLanguageRedirect for clientLocale === english', () => {
|
||||
'https://www.freecodecamp.org/chinese-traditional/settings';
|
||||
const dothrakiPageURL = 'https://www.freecodecamp.org/dothraki/settings';
|
||||
|
||||
const originalLocation = window.location;
|
||||
|
||||
beforeEach(() => {
|
||||
Object.defineProperty(window, 'location', {
|
||||
writable: true,
|
||||
@@ -69,10 +63,6 @@ describe('createLanguageRedirect for clientLocale === english', () => {
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
window.location = originalLocation;
|
||||
});
|
||||
|
||||
[
|
||||
{ lang: 'english', url: currentPageURL },
|
||||
{ lang: 'chinese', url: chinesePageURL },
|
||||
@@ -113,8 +103,6 @@ describe('createLanguageRedirect for clientLocale === chinese', () => {
|
||||
const dothrakiPageURL =
|
||||
'https://www.freecodecamp.org/dothraki/learn/responsive-web-design/basic-html-and-html5/inform-with-the-paragraph-element';
|
||||
|
||||
const originalLocation = window.location;
|
||||
|
||||
beforeEach(() => {
|
||||
Object.defineProperty(window, 'location', {
|
||||
writable: true,
|
||||
@@ -122,10 +110,6 @@ describe('createLanguageRedirect for clientLocale === chinese', () => {
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
window.location = originalLocation;
|
||||
});
|
||||
|
||||
[
|
||||
{ lang: 'chinese', url: currentPageURL },
|
||||
{ lang: 'english', url: englishPageURL },
|
||||
@@ -156,8 +140,6 @@ describe('createLanguageRedirect for clientLocale === chinese', () => {
|
||||
'https://www.freecodecamp.org/chinese-traditional/settings';
|
||||
const dothrakiPageURL = 'https://www.freecodecamp.org/dothraki/settings';
|
||||
|
||||
const originalLocation = window.location;
|
||||
|
||||
beforeEach(() => {
|
||||
Object.defineProperty(window, 'location', {
|
||||
writable: true,
|
||||
@@ -165,10 +147,6 @@ describe('createLanguageRedirect for clientLocale === chinese', () => {
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
window.location = originalLocation;
|
||||
});
|
||||
|
||||
[
|
||||
{ lang: 'chinese', url: currentPageURL },
|
||||
{ lang: 'english', url: englishPageURL },
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { describe, test, expect } from 'vitest';
|
||||
import {
|
||||
editorValidator,
|
||||
fCCValidator,
|
||||
@@ -6,146 +7,148 @@ import {
|
||||
pathValidator
|
||||
} from './form-validators';
|
||||
|
||||
test('editorValidator', () => {
|
||||
const editorAddresses = [
|
||||
'repl.it/@username',
|
||||
'repl.it/join/username',
|
||||
'replit.com/@username',
|
||||
'replit.com/join/username',
|
||||
'glitch.com/edit/#!',
|
||||
'codesandbox.io/s/',
|
||||
'https://github.com',
|
||||
'https://glitch.com/edit/#!/project',
|
||||
'https://glitch.com/edit/#!'
|
||||
];
|
||||
describe('Form validators', () => {
|
||||
test('editorValidator', () => {
|
||||
const editorAddresses = [
|
||||
'repl.it/@username',
|
||||
'repl.it/join/username',
|
||||
'replit.com/@username',
|
||||
'replit.com/join/username',
|
||||
'glitch.com/edit/#!',
|
||||
'codesandbox.io/s/',
|
||||
'https://github.com',
|
||||
'https://glitch.com/edit/#!/project',
|
||||
'https://glitch.com/edit/#!'
|
||||
];
|
||||
|
||||
const nonEditorAddresses = [
|
||||
'example.com',
|
||||
'http://example.com',
|
||||
'https://example.com',
|
||||
'http://repl.it',
|
||||
'http://glitch.com',
|
||||
'https://repl.it/home'
|
||||
];
|
||||
const nonEditorAddresses = [
|
||||
'example.com',
|
||||
'http://example.com',
|
||||
'https://example.com',
|
||||
'http://repl.it',
|
||||
'http://glitch.com',
|
||||
'https://repl.it/home'
|
||||
];
|
||||
|
||||
editorAddresses.forEach(address => {
|
||||
expect(editorValidator(address)).not.toBeNull();
|
||||
editorAddresses.forEach(address => {
|
||||
expect(editorValidator(address)).not.toBeNull();
|
||||
});
|
||||
|
||||
nonEditorAddresses.forEach(address => {
|
||||
expect(editorValidator(address)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
nonEditorAddresses.forEach(address => {
|
||||
expect(editorValidator(address)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
test('fCCValidator', () => {
|
||||
const fCCAddresses = [
|
||||
'codepen.io/freecodecamp',
|
||||
'freecodecamp.rocks',
|
||||
'github.com/freecodecamp',
|
||||
'.freecodecamp.org',
|
||||
'https://codepen.io/freecodecamp',
|
||||
'https://freecodecamp.rocks',
|
||||
'https://github.com/freeCodeCamp',
|
||||
'https://www.freecodecamp.org'
|
||||
];
|
||||
|
||||
const nonFCCAddresses = [
|
||||
'example.com',
|
||||
'http://example.com',
|
||||
'https://example.com',
|
||||
'http://codepen.io',
|
||||
'https://github.com',
|
||||
'https://codepen.io/editor',
|
||||
'https://github.com/repo'
|
||||
];
|
||||
|
||||
fCCAddresses.forEach(address => {
|
||||
expect(fCCValidator(address)).not.toBeNull();
|
||||
});
|
||||
|
||||
nonFCCAddresses.forEach(address => {
|
||||
expect(fCCValidator(address)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
test('localhostValidator', () => {
|
||||
const privateAddresses = [
|
||||
'http://localhost:3000',
|
||||
'https://localhost:3000',
|
||||
'http://127.0.0.1',
|
||||
'http://127.0.0.1:3000',
|
||||
'https://127.0.0.1',
|
||||
'https://127.0.0.1:3000',
|
||||
'http://[::1]:3000'
|
||||
];
|
||||
|
||||
const publicAddresses = [
|
||||
'http://localhost.com',
|
||||
'http://example.com',
|
||||
'http://example.com:3000',
|
||||
'https://example.com',
|
||||
'https://example.com:3000'
|
||||
];
|
||||
|
||||
privateAddresses.forEach(address => {
|
||||
expect(localhostValidator(address)).not.toBeNull();
|
||||
});
|
||||
|
||||
publicAddresses.forEach(address => {
|
||||
expect(localhostValidator(address)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
test('httpValidator', () => {
|
||||
const allowedHttpAddresses = ['http://[::1]:3000', 'http://localhost:3000'];
|
||||
|
||||
const disallowedHttpAddresses = [
|
||||
'http://example.com',
|
||||
'http://localhost.com'
|
||||
];
|
||||
|
||||
const nonHttpAddresses = [
|
||||
'ftp://example.com',
|
||||
'file://localhost',
|
||||
'ws://example.com',
|
||||
'wss://localhost',
|
||||
'https://example.com/test',
|
||||
'https://localhost.com'
|
||||
];
|
||||
|
||||
allowedHttpAddresses.forEach(address => {
|
||||
expect(httpValidator(address)).toBeNull();
|
||||
});
|
||||
|
||||
disallowedHttpAddresses.forEach(address => {
|
||||
expect(httpValidator(address)).not.toBeNull();
|
||||
});
|
||||
|
||||
nonHttpAddresses.forEach(address => {
|
||||
expect(httpValidator(address)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
test('pathValidator', () => {
|
||||
const rootAddresses = [
|
||||
'http://example.com/',
|
||||
'http://example.com',
|
||||
'https://example.com/',
|
||||
'https://localhost.com'
|
||||
];
|
||||
|
||||
const nonRootAddresses = [
|
||||
'http://example.com/path',
|
||||
'http://example.com/path/',
|
||||
'https://example.com/path',
|
||||
'https://example.com/path/'
|
||||
];
|
||||
|
||||
rootAddresses.forEach(address => {
|
||||
expect(pathValidator(address)).toBeNull();
|
||||
});
|
||||
|
||||
nonRootAddresses.forEach(address => {
|
||||
expect(pathValidator(address)).not.toBeNull();
|
||||
test('fCCValidator', () => {
|
||||
const fCCAddresses = [
|
||||
'codepen.io/freecodecamp',
|
||||
'freecodecamp.rocks',
|
||||
'github.com/freecodecamp',
|
||||
'.freecodecamp.org',
|
||||
'https://codepen.io/freecodecamp',
|
||||
'https://freecodecamp.rocks',
|
||||
'https://github.com/freeCodeCamp',
|
||||
'https://www.freecodecamp.org'
|
||||
];
|
||||
|
||||
const nonFCCAddresses = [
|
||||
'example.com',
|
||||
'http://example.com',
|
||||
'https://example.com',
|
||||
'http://codepen.io',
|
||||
'https://github.com',
|
||||
'https://codepen.io/editor',
|
||||
'https://github.com/repo'
|
||||
];
|
||||
|
||||
fCCAddresses.forEach(address => {
|
||||
expect(fCCValidator(address)).not.toBeNull();
|
||||
});
|
||||
|
||||
nonFCCAddresses.forEach(address => {
|
||||
expect(fCCValidator(address)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
test('localhostValidator', () => {
|
||||
const privateAddresses = [
|
||||
'http://localhost:3000',
|
||||
'https://localhost:3000',
|
||||
'http://127.0.0.1',
|
||||
'http://127.0.0.1:3000',
|
||||
'https://127.0.0.1',
|
||||
'https://127.0.0.1:3000',
|
||||
'http://[::1]:3000'
|
||||
];
|
||||
|
||||
const publicAddresses = [
|
||||
'http://localhost.com',
|
||||
'http://example.com',
|
||||
'http://example.com:3000',
|
||||
'https://example.com',
|
||||
'https://example.com:3000'
|
||||
];
|
||||
|
||||
privateAddresses.forEach(address => {
|
||||
expect(localhostValidator(address)).not.toBeNull();
|
||||
});
|
||||
|
||||
publicAddresses.forEach(address => {
|
||||
expect(localhostValidator(address)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
test('httpValidator', () => {
|
||||
const allowedHttpAddresses = ['http://[::1]:3000', 'http://localhost:3000'];
|
||||
|
||||
const disallowedHttpAddresses = [
|
||||
'http://example.com',
|
||||
'http://localhost.com'
|
||||
];
|
||||
|
||||
const nonHttpAddresses = [
|
||||
'ftp://example.com',
|
||||
'file://localhost',
|
||||
'ws://example.com',
|
||||
'wss://localhost',
|
||||
'https://example.com/test',
|
||||
'https://localhost.com'
|
||||
];
|
||||
|
||||
allowedHttpAddresses.forEach(address => {
|
||||
expect(httpValidator(address)).toBeNull();
|
||||
});
|
||||
|
||||
disallowedHttpAddresses.forEach(address => {
|
||||
expect(httpValidator(address)).not.toBeNull();
|
||||
});
|
||||
|
||||
nonHttpAddresses.forEach(address => {
|
||||
expect(httpValidator(address)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
test('pathValidator', () => {
|
||||
const rootAddresses = [
|
||||
'http://example.com/',
|
||||
'http://example.com',
|
||||
'https://example.com/',
|
||||
'https://localhost.com'
|
||||
];
|
||||
|
||||
const nonRootAddresses = [
|
||||
'http://example.com/path',
|
||||
'http://example.com/path/',
|
||||
'https://example.com/path',
|
||||
'https://example.com/path/'
|
||||
];
|
||||
|
||||
rootAddresses.forEach(address => {
|
||||
expect(pathValidator(address)).toBeNull();
|
||||
});
|
||||
|
||||
nonRootAddresses.forEach(address => {
|
||||
expect(pathValidator(address)).not.toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { describe, test, expect, vi } from 'vitest';
|
||||
import { render, fireEvent, screen } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
|
||||
@@ -20,69 +21,71 @@ const defaultTestProps: StrictSolutionFormProps = {
|
||||
submit: () => undefined
|
||||
};
|
||||
|
||||
test('should render', () => {
|
||||
render(<StrictSolutionForm {...defaultTestProps} />);
|
||||
describe('<StrictSolutionForm />', () => {
|
||||
test('should render', () => {
|
||||
render(<StrictSolutionForm {...defaultTestProps} />);
|
||||
|
||||
const websiteInput = screen.getByLabelText(/WebSite label/);
|
||||
expect(websiteInput).toBeRequired();
|
||||
expect(websiteInput).toHaveAttribute('type', 'url');
|
||||
const websiteInput = screen.getByLabelText(/WebSite label/);
|
||||
expect(websiteInput).toBeRequired();
|
||||
expect(websiteInput).toHaveAttribute('type', 'url');
|
||||
|
||||
const button = screen.getByText(/submit/i);
|
||||
expect(button).toHaveAttribute('type', 'submit');
|
||||
expect(button).toHaveAttribute('aria-disabled', 'true');
|
||||
});
|
||||
|
||||
test('should render with default values', () => {
|
||||
const websiteValue = 'http://mysite.com';
|
||||
const nameValue = 'John';
|
||||
|
||||
render(
|
||||
<StrictSolutionForm
|
||||
{...defaultTestProps}
|
||||
enableSubmit={true}
|
||||
initialValues={{ name: nameValue, website: websiteValue }}
|
||||
/>
|
||||
);
|
||||
|
||||
const nameInput = screen.getByLabelText(/name Label/);
|
||||
expect(nameInput).toHaveValue(nameValue);
|
||||
|
||||
const websiteInput = screen.getByLabelText(/WebSite label/);
|
||||
expect(websiteInput).toHaveValue(websiteValue);
|
||||
|
||||
const button = screen.getByText(/submit/i);
|
||||
expect(button).toBeEnabled();
|
||||
});
|
||||
|
||||
test('should submit', () => {
|
||||
const submit = jest.fn();
|
||||
const props = {
|
||||
...defaultTestProps,
|
||||
submit
|
||||
};
|
||||
const websiteValue = 'http://mysite.com';
|
||||
|
||||
render(<StrictSolutionForm {...props} />);
|
||||
|
||||
const websiteInput = screen.getByLabelText(/WebSite label/);
|
||||
fireEvent.change(websiteInput, { target: { value: websiteValue } });
|
||||
expect(websiteInput).toHaveValue(websiteValue);
|
||||
|
||||
const button = screen.getByText(/submit/i);
|
||||
expect(button).toBeEnabled();
|
||||
|
||||
fireEvent.click(button);
|
||||
expect(submit).toHaveBeenCalledTimes(1);
|
||||
expect((submit.mock.calls[0] as unknown[])[0]).toEqual(
|
||||
expect.objectContaining({ values: { website: websiteValue } })
|
||||
);
|
||||
|
||||
fireEvent.change(websiteInput, { target: { value: `${websiteValue}///` } });
|
||||
expect(websiteInput).toHaveValue(`${websiteValue}///`);
|
||||
|
||||
fireEvent.click(button);
|
||||
expect(submit).toHaveBeenCalledTimes(2);
|
||||
expect((submit.mock.calls[1] as unknown[])[0]).toEqual(
|
||||
expect.objectContaining({ values: { website: websiteValue } })
|
||||
);
|
||||
const button = screen.getByText(/submit/i);
|
||||
expect(button).toHaveAttribute('type', 'submit');
|
||||
expect(button).toHaveAttribute('aria-disabled', 'true');
|
||||
});
|
||||
|
||||
test('should render with default values', () => {
|
||||
const websiteValue = 'http://mysite.com';
|
||||
const nameValue = 'John';
|
||||
|
||||
render(
|
||||
<StrictSolutionForm
|
||||
{...defaultTestProps}
|
||||
enableSubmit={true}
|
||||
initialValues={{ name: nameValue, website: websiteValue }}
|
||||
/>
|
||||
);
|
||||
|
||||
const nameInput = screen.getByLabelText(/name Label/);
|
||||
expect(nameInput).toHaveValue(nameValue);
|
||||
|
||||
const websiteInput = screen.getByLabelText(/WebSite label/);
|
||||
expect(websiteInput).toHaveValue(websiteValue);
|
||||
|
||||
const button = screen.getByText(/submit/i);
|
||||
expect(button).toBeEnabled();
|
||||
});
|
||||
|
||||
test('should submit', () => {
|
||||
const submit = vi.fn();
|
||||
const props = {
|
||||
...defaultTestProps,
|
||||
submit
|
||||
};
|
||||
const websiteValue = 'http://mysite.com';
|
||||
|
||||
render(<StrictSolutionForm {...props} />);
|
||||
|
||||
const websiteInput = screen.getByLabelText(/WebSite label/);
|
||||
fireEvent.change(websiteInput, { target: { value: websiteValue } });
|
||||
expect(websiteInput).toHaveValue(websiteValue);
|
||||
|
||||
const button = screen.getByText(/submit/i);
|
||||
expect(button).toBeEnabled();
|
||||
|
||||
fireEvent.click(button);
|
||||
expect(submit).toHaveBeenCalledTimes(1);
|
||||
expect((submit.mock.calls[0] as unknown[])[0]).toEqual(
|
||||
expect.objectContaining({ values: { website: websiteValue } })
|
||||
);
|
||||
|
||||
fireEvent.change(websiteInput, { target: { value: `${websiteValue}///` } });
|
||||
expect(websiteInput).toHaveValue(`${websiteValue}///`);
|
||||
|
||||
fireEvent.click(button);
|
||||
expect(submit).toHaveBeenCalledTimes(2);
|
||||
expect((submit.mock.calls[1] as unknown[])[0]).toEqual(
|
||||
expect.objectContaining({ values: { website: websiteValue } })
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,35 +1,23 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`<Loader /> matches the fullScreen render snapshot 1`] = `
|
||||
exports[`<Loader /> > matches the fullScreen render snapshot 1`] = `
|
||||
<div
|
||||
class="fcc-loader full-screen-wrapper"
|
||||
data-testid="fcc-loader"
|
||||
>
|
||||
<div
|
||||
class="sk-fade-in sk-spinner line-scale-pulse-out"
|
||||
>
|
||||
<div />
|
||||
<div />
|
||||
<div />
|
||||
<div />
|
||||
<div />
|
||||
<div>
|
||||
Spinner
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`<Loader /> matches to the default render snapshot 1`] = `
|
||||
exports[`<Loader /> > matches to the default render snapshot 1`] = `
|
||||
<div
|
||||
class="fcc-loader "
|
||||
data-testid="fcc-loader"
|
||||
>
|
||||
<div
|
||||
class="sk-fade-in sk-spinner line-scale-pulse-out"
|
||||
>
|
||||
<div />
|
||||
<div />
|
||||
<div />
|
||||
<div />
|
||||
<div />
|
||||
<div>
|
||||
Spinner
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import borderColorPicker from './border-color-picker';
|
||||
|
||||
describe('Border color picker helper', () => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`<BlockSaveButton /> snapshot 1`] = `
|
||||
exports[`<BlockSaveButton /> > <BlockSaveButton /> snapshot 1`] = `
|
||||
<div>
|
||||
<button
|
||||
class=" relative inline-block mt-[0.5px] border-solid border-3 active:before:w-full active:before:h-full active:before:absolute active:before:inset-0 active:before:border-3 active:before:border-transparent active:before:bg-gray-900 active:before:opacity-20 text-center cursor-pointer no-underline block w-full bg-background-quaternary text-foreground-secondary border-foreground-secondary hover:bg-foreground-primary hover:text-background-primary hover:border-foreground-secondary dark:hover:bg-background-primary dark:hover:text-foreground-primary px-3 py-1.5 text-md"
|
||||
|
||||
@@ -1,23 +1,26 @@
|
||||
import { describe, test, expect } from 'vitest';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
|
||||
import BlockSaveButton from './block-save-button';
|
||||
|
||||
test('<BlockSaveButton /> snapshot', () => {
|
||||
const { container } = render(<BlockSaveButton />);
|
||||
describe('<BlockSaveButton />', () => {
|
||||
test('<BlockSaveButton /> snapshot', () => {
|
||||
const { container } = render(<BlockSaveButton />);
|
||||
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('Button text should default to the correct translation key', () => {
|
||||
render(<BlockSaveButton />);
|
||||
|
||||
expect(screen.getByRole('button')).toHaveTextContent('buttons.save');
|
||||
});
|
||||
|
||||
test('Button text should match "children"', () => {
|
||||
const testText = 'My Text Here';
|
||||
render(<BlockSaveButton>{testText}</BlockSaveButton>);
|
||||
|
||||
expect(screen.getByRole('button')).toHaveTextContent(testText);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('Button text should default to the correct translation key', () => {
|
||||
render(<BlockSaveButton />);
|
||||
|
||||
expect(screen.getByRole('button')).toHaveTextContent('buttons.save');
|
||||
});
|
||||
|
||||
test('Button text should match "children"', () => {
|
||||
const testText = 'My Text Here';
|
||||
render(<BlockSaveButton>{testText}</BlockSaveButton>);
|
||||
|
||||
expect(screen.getByRole('button')).toHaveTextContent(testText);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import React from 'react';
|
||||
import { create } from 'react-test-renderer';
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`<Profile/> renders correctly 1`] = `
|
||||
exports[`<Profile/> > renders correctly 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="h-[30px]"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`should check certification page consistency 1`] = `
|
||||
exports[`Timeline buttons test > should check certification page consistency 1`] = `
|
||||
<div
|
||||
className="mx-[-15px] "
|
||||
>
|
||||
@@ -1,3 +1,12 @@
|
||||
import {
|
||||
describe,
|
||||
it,
|
||||
expect,
|
||||
beforeEach,
|
||||
afterEach,
|
||||
vi,
|
||||
MockInstance
|
||||
} from 'vitest';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
|
||||
@@ -24,11 +33,10 @@ props.calendar[date1] = 1;
|
||||
props.calendar[date2] = 1;
|
||||
props.calendar[date3] = 1;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
let dateNowMockFn: jest.MockInstance<any, unknown[]>;
|
||||
let dateNowMockFn: MockInstance;
|
||||
|
||||
beforeEach(() => {
|
||||
dateNowMockFn = jest.spyOn(Date, 'now').mockImplementation(() => now);
|
||||
dateNowMockFn = vi.spyOn(Date, 'now').mockImplementation(() => now);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { describe, it, test, expect, beforeEach, vi } from 'vitest';
|
||||
import { render, screen, within } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import Stats, { calculateStreaks } from './stats';
|
||||
@@ -69,10 +70,10 @@ const multipleEntriesInOneDay = {
|
||||
'1736946300': 1 // 2025-01-15 13:05:00 UTC
|
||||
};
|
||||
|
||||
jest.useFakeTimers();
|
||||
vi.useFakeTimers();
|
||||
|
||||
describe('calculateStreaks', () => {
|
||||
beforeEach(() => jest.setSystemTime(new Date(2025, 0, 15)));
|
||||
beforeEach(() => vi.setSystemTime(new Date(2025, 0, 15)));
|
||||
test('Should return 0 for the current streak if the user has not made progress today', () => {
|
||||
const { longestStreak, currentStreak } =
|
||||
calculateStreaks(oldStreakCalendar);
|
||||
@@ -82,7 +83,7 @@ describe('calculateStreaks', () => {
|
||||
});
|
||||
|
||||
test('Should calculate longest streak, regardless of how long ago they were', () => {
|
||||
jest.setSystemTime(new Date(2030, 0, 15));
|
||||
vi.setSystemTime(new Date(2030, 0, 15));
|
||||
const { longestStreak, currentStreak } =
|
||||
calculateStreaks(oldStreakCalendar);
|
||||
|
||||
@@ -91,7 +92,7 @@ describe('calculateStreaks', () => {
|
||||
});
|
||||
|
||||
test('Should return a longest streak of 3 days when the current streak is 3 days', () => {
|
||||
jest.setSystemTime(new Date(2025, 0, 14));
|
||||
vi.setSystemTime(new Date(2025, 0, 14));
|
||||
const { longestStreak, currentStreak } =
|
||||
calculateStreaks(recentStreakCalendar);
|
||||
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
||||
import { useStaticQuery } from 'gatsby';
|
||||
import React from 'react';
|
||||
import { beforeEach, describe, it, expect, vi } from 'vitest';
|
||||
|
||||
vi.mock('../../../utils/get-words');
|
||||
|
||||
import { render, screen } from '../../../../utils/test-utils';
|
||||
import { createStore } from '../../../redux/create-store';
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
import React from 'react';
|
||||
import renderer from 'react-test-renderer';
|
||||
import { Provider } from 'react-redux';
|
||||
|
||||
import { createStore } from '../../../redux/create-store';
|
||||
import completedChallenges from '../../../__mocks__/completed-challenges.json';
|
||||
import Timeline from './time-line';
|
||||
|
||||
Date.prototype.toLocaleString = jest.fn(() => 'Dec 29, 2022');
|
||||
Date.prototype.toISOString = jest.fn(() => '2016-09-28T20:31:56.730Z');
|
||||
|
||||
jest.mock('../../../analytics');
|
||||
|
||||
jest.mock('gatsby', () => {
|
||||
const edges = require('../../../__mocks__/edges.json');
|
||||
const React = require('react');
|
||||
const gatsby = jest.requireActual('gatsby');
|
||||
return {
|
||||
...gatsby,
|
||||
useStaticQuery: jest.fn().mockReturnValue({
|
||||
allChallengeNode: {
|
||||
edges: edges
|
||||
}
|
||||
}),
|
||||
graphql: jest.fn(),
|
||||
Link: jest.fn().mockImplementation(({ to, ...rest }) =>
|
||||
React.createElement('a', {
|
||||
...rest,
|
||||
href: to,
|
||||
gatsby: 'true'
|
||||
})
|
||||
)
|
||||
};
|
||||
});
|
||||
|
||||
it('should check certification page consistency', () => {
|
||||
const tree = renderer
|
||||
.create(
|
||||
<Provider store={createStore()}>
|
||||
<Timeline
|
||||
completedMap={completedChallenges}
|
||||
username='CeritifedUser'
|
||||
onPress={() => {}}
|
||||
/>
|
||||
</Provider>
|
||||
)
|
||||
.toJSON();
|
||||
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
@@ -0,0 +1,55 @@
|
||||
import React from 'react';
|
||||
import renderer from 'react-test-renderer';
|
||||
import { Provider } from 'react-redux';
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
|
||||
import { createStore } from '../../../redux/create-store';
|
||||
import completedChallenges from './__fixtures__/completed-challenges.json';
|
||||
import Timeline from './time-line';
|
||||
|
||||
Date.prototype.toLocaleString = vi.fn(() => 'Dec 29, 2022');
|
||||
Date.prototype.toISOString = vi.fn(() => '2016-09-28T20:31:56.730Z');
|
||||
|
||||
vi.mock('../../../analytics');
|
||||
|
||||
vi.mock('../../../utils/get-words');
|
||||
|
||||
vi.mock('gatsby', async () => {
|
||||
const edges = require('./__fixtures__/edges.json');
|
||||
const React = require('react');
|
||||
const gatsby = await vi.importActual('gatsby');
|
||||
return {
|
||||
...gatsby,
|
||||
useStaticQuery: vi.fn().mockReturnValue({
|
||||
allChallengeNode: {
|
||||
edges: edges
|
||||
}
|
||||
}),
|
||||
graphql: vi.fn(),
|
||||
Link: vi.fn().mockImplementation(({ to, ...rest }) =>
|
||||
React.createElement('a', {
|
||||
...rest,
|
||||
href: to,
|
||||
gatsby: 'true'
|
||||
})
|
||||
)
|
||||
};
|
||||
});
|
||||
|
||||
describe('Timeline buttons test', () => {
|
||||
it('should check certification page consistency', () => {
|
||||
const tree = renderer
|
||||
.create(
|
||||
<Provider store={createStore()}>
|
||||
<Timeline
|
||||
completedMap={completedChallenges}
|
||||
username='CeritifedUser'
|
||||
onPress={() => {}}
|
||||
/>
|
||||
</Provider>
|
||||
)
|
||||
.toJSON();
|
||||
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
@@ -1,3 +1,4 @@
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
@@ -5,7 +6,7 @@ import { createStore } from 'redux';
|
||||
import { UserThemes } from '../../redux/types';
|
||||
import Profile from './profile';
|
||||
|
||||
jest.mock('../../analytics');
|
||||
vi.mock('../../analytics');
|
||||
//workaround to avoid some strange gatsby error:
|
||||
window.___loader = { enqueue: () => {}, hovering: () => {} };
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
import ShallowRenderer from 'react-test-renderer/shallow';
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
|
||||
import { SearchBar } from './search-bar';
|
||||
|
||||
@@ -19,7 +20,7 @@ describe('<SearchBar />', () => {
|
||||
});
|
||||
|
||||
const searchBarProps = {
|
||||
toggleSearchDropdown: jest.fn(),
|
||||
toggleSearchFocused: jest.fn(),
|
||||
updateSearchQuery: jest.fn()
|
||||
toggleSearchDropdown: vi.fn(),
|
||||
toggleSearchFocused: vi.fn(),
|
||||
updateSearchQuery: vi.fn()
|
||||
};
|
||||
|
||||
@@ -1,21 +1,24 @@
|
||||
import React from 'react';
|
||||
import * as Gatsby from 'gatsby';
|
||||
import { render } from '@testing-library/react';
|
||||
import Helmet from 'react-helmet';
|
||||
import { describe, it, expect, vi, afterEach } from 'vitest';
|
||||
|
||||
import SEO from './index';
|
||||
|
||||
const useStaticQuery = jest.spyOn(Gatsby, `useStaticQuery`);
|
||||
const mockUseStaticQuery = {
|
||||
site: {
|
||||
siteMetadata: {
|
||||
title: 'freeCodeCamp',
|
||||
siteUrl: 'freeCodeCamp.org'
|
||||
title: 'freeCodeCamp'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
jest.mock('react-i18next', () => ({
|
||||
vi.mock('gatsby', () => ({
|
||||
useStaticQuery: vi.fn(() => mockUseStaticQuery),
|
||||
graphql: vi.fn()
|
||||
}));
|
||||
|
||||
vi.mock('react-i18next', () => ({
|
||||
useTranslation: () => {
|
||||
return {
|
||||
t: (str: string) => ({
|
||||
@@ -27,12 +30,8 @@ jest.mock('react-i18next', () => ({
|
||||
}));
|
||||
|
||||
describe('<SEO />', () => {
|
||||
beforeEach(() => {
|
||||
useStaticQuery.mockImplementation(() => mockUseStaticQuery);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it('renders', () => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`<Honesty /> <Honesty /> snapshot when isHonest is false: Honesty 1`] = `
|
||||
exports[`<Honesty /> > <Honesty /> snapshot when isHonest is false > Honesty 1`] = `
|
||||
<DocumentFragment>
|
||||
<section
|
||||
id="honesty-policy"
|
||||
@@ -67,7 +67,7 @@ exports[`<Honesty /> <Honesty /> snapshot when isHonest is false: Honesty 1`] =
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`<Honesty /> <Honesty /> snapshot when isHonest is true: HonestyAccepted 1`] = `
|
||||
exports[`<Honesty /> > <Honesty /> snapshot when isHonest is true > HonestyAccepted 1`] = `
|
||||
<DocumentFragment>
|
||||
<section
|
||||
id="honesty-policy"
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
|
||||
vi.mock('../../utils/get-words');
|
||||
|
||||
import { createStore } from '../../redux/create-store';
|
||||
import { Ext } from '../../redux/prop-types';
|
||||
import { verifyCert } from '../../redux/settings/actions';
|
||||
@@ -8,7 +12,7 @@ import { createFlashMessage } from '../Flash/redux';
|
||||
|
||||
import CertificationSettings from './certification';
|
||||
|
||||
jest.mock('../../analytics');
|
||||
vi.mock('../../analytics');
|
||||
|
||||
function renderWithRedux(ui: JSX.Element) {
|
||||
return render(<Provider store={createStore()}>{ui}</Provider>);
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import React from 'react';
|
||||
import { render, fireEvent, screen } from '@testing-library/react';
|
||||
import { describe, test, expect, vi } from 'vitest';
|
||||
import Honesty from './honesty';
|
||||
|
||||
describe('<Honesty />', () => {
|
||||
const updateIsHonestMock = jest.fn();
|
||||
const updateIsHonestMock = vi.fn();
|
||||
|
||||
test('<Honesty /> snapshot when isHonest is false', () => {
|
||||
const { asFragment } = render(
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { describe, test, expect } from 'vitest';
|
||||
import { ShareTemplate } from './share-template';
|
||||
|
||||
const redirectURL = 'string';
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { describe, test, expect } from 'vitest';
|
||||
import {
|
||||
hastag,
|
||||
nextLine,
|
||||
@@ -9,29 +11,37 @@ import {
|
||||
threadsData
|
||||
} from './use-share';
|
||||
|
||||
test('useShare testing', () => {
|
||||
const superBlock = 'testSuperBlock';
|
||||
const block = 'testBlock';
|
||||
const { t } = useTranslation();
|
||||
describe('useShare', () => {
|
||||
test('useShare hook returns correct social media URLs', () => {
|
||||
const superBlock = 'testSuperBlock';
|
||||
const block = 'testBlock';
|
||||
|
||||
const redirectURL = useShare({
|
||||
superBlock: superBlock,
|
||||
block: block
|
||||
const { result: translationResult } = renderHook(() => useTranslation());
|
||||
const { t } = translationResult.current;
|
||||
|
||||
// Test useShare hook
|
||||
const { result: shareResult } = renderHook(() =>
|
||||
useShare({
|
||||
superBlock,
|
||||
block
|
||||
})
|
||||
);
|
||||
|
||||
const freecodecampLearnDomain = 'www.freecodecamp.org/learn';
|
||||
const i18nSupportedBlock = t(`intro:${superBlock}.blocks.${block}.title`);
|
||||
const tweetMessage = `I${space}have${space}completed${space}${i18nSupportedBlock}${space}${hastag}freecodecamp`;
|
||||
const redirectFreeCodeCampLearnURL = `https://${freecodecampLearnDomain}/${superBlock}/${hastag}${block}`;
|
||||
|
||||
expect(shareResult.current.xUrl).toBe(
|
||||
`https://${twitterData.domain}/${twitterData.action}?original_referer=${twitterData.developerDomainURL}&text=${tweetMessage}${nextLine}&url=${redirectFreeCodeCampLearnURL}`
|
||||
);
|
||||
|
||||
expect(shareResult.current.blueSkyUrl).toBe(
|
||||
`https://${blueSkyData.domain}/${blueSkyData.action}?original_referer=${blueSkyData.developerDomainURL}&text=${tweetMessage}${nextLine}&url=${redirectFreeCodeCampLearnURL}`
|
||||
);
|
||||
|
||||
expect(shareResult.current.threadsURL).toBe(
|
||||
`https://${threadsData.domain}/${threadsData.action}?original_referer=${threadsData.developerDomainURL}&text=${tweetMessage}${nextLine}&url=${redirectFreeCodeCampLearnURL}`
|
||||
);
|
||||
});
|
||||
|
||||
const freecodecampLearnDomain = 'www.freecodecamp.org/learn';
|
||||
const i18nSupportedBlock = t(`intro:${superBlock}.blocks.${block}.title`);
|
||||
const tweetMessage = `I${space}have${space}completed${space}${i18nSupportedBlock}${space}%23freecodecamp`;
|
||||
const redirectFreeCodeCampLearnURL = `https://${freecodecampLearnDomain}/${superBlock}/${hastag}${block}`;
|
||||
expect(redirectURL.xUrl).toBe(
|
||||
`https://${twitterData.domain}/${twitterData.action}?original_referer=${twitterData.developerDomainURL}&text=${tweetMessage}${nextLine}&url=${redirectFreeCodeCampLearnURL}`
|
||||
);
|
||||
|
||||
expect(redirectURL.blueSkyUrl).toBe(
|
||||
`https://${blueSkyData.domain}/${blueSkyData.action}?original_referer=${blueSkyData.developerDomainURL}&text=${tweetMessage}${nextLine}&url=${redirectFreeCodeCampLearnURL}`
|
||||
);
|
||||
|
||||
expect(redirectURL.threadsURL).toBe(
|
||||
`https://${threadsData.domain}/${threadsData.action}?original_referer=${threadsData.developerDomainURL}&text=${tweetMessage}${nextLine}&url=${redirectFreeCodeCampLearnURL}`
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import React from 'react';
|
||||
import { render, screen, fireEvent } from '@testing-library/react';
|
||||
import store from 'store';
|
||||
import { describe, it, expect, beforeAll, afterEach, vi } from 'vitest';
|
||||
import StagingWarningModal from '.';
|
||||
|
||||
describe('StagingWarningModal', () => {
|
||||
beforeAll(() => {
|
||||
// The Modal component uses `ResizeObserver` under the hood.
|
||||
@@ -9,16 +11,16 @@ describe('StagingWarningModal', () => {
|
||||
// Ref: https://github.com/jsdom/jsdom/issues/3368
|
||||
Object.defineProperty(window, 'ResizeObserver', {
|
||||
writable: true,
|
||||
value: jest.fn(() => ({
|
||||
observe: jest.fn(),
|
||||
unobserve: jest.fn(),
|
||||
disconnect: jest.fn()
|
||||
value: vi.fn(() => ({
|
||||
observe: vi.fn(),
|
||||
unobserve: vi.fn(),
|
||||
disconnect: vi.fn()
|
||||
}))
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('renders the modal successfully', () => {
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
import { render } from '@testing-library/react';
|
||||
import { navigate, withPrefix } from 'gatsby';
|
||||
import React from 'react';
|
||||
import { describe, it, expect } from 'vitest';
|
||||
|
||||
import Challenges from './challenges';
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
// @vitest-environment jsdom
|
||||
import { expectSaga } from 'redux-saga-test-plan';
|
||||
import { describe, it, vi } from 'vitest';
|
||||
import {
|
||||
postChargeStripe,
|
||||
postChargeStripeCard,
|
||||
@@ -18,11 +20,11 @@ import {
|
||||
updateCardError
|
||||
} from './actions';
|
||||
|
||||
jest.mock('../utils/ajax');
|
||||
jest.mock('../analytics/call-ga');
|
||||
jest.mock('../utils/stripe', () => ({
|
||||
vi.mock('../utils/ajax');
|
||||
vi.mock('../analytics/call-ga');
|
||||
vi.mock('../utils/stripe', () => ({
|
||||
stripe: Promise.resolve({
|
||||
redirectToCheckout: jest.fn()
|
||||
redirectToCheckout: vi.fn()
|
||||
})
|
||||
}));
|
||||
|
||||
@@ -32,7 +34,7 @@ const postChargeDataMock = {
|
||||
paymentContext: 'donate page',
|
||||
amount: '500',
|
||||
duration: 'month',
|
||||
handleAuthentication: jest.fn(),
|
||||
handleAuthentication: vi.fn(),
|
||||
paymentMethodId: '123456'
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { ActionsObservable, StateObservable } from 'redux-observable';
|
||||
import { Subject } from 'rxjs';
|
||||
import store from 'store';
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { actionTypes } from './action-types';
|
||||
import failedUpdatesEpic from './failed-updates-epic';
|
||||
|
||||
jest.mock('../analytics');
|
||||
vi.mock('../analytics');
|
||||
|
||||
const key = 'fcc-failed-updates';
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import type {
|
||||
ChallengeFile,
|
||||
SavedChallengeFile
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`<ChallengeTitle/> renders correctly 1`] = `
|
||||
exports[`<ChallengeTitle/> > renders correctly 1`] = `
|
||||
<div
|
||||
className="challenge-title-wrap"
|
||||
>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
import renderer from 'react-test-renderer';
|
||||
import { describe, it, expect } from 'vitest';
|
||||
|
||||
import ChallengeTitle from './challenge-title';
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import store from 'store';
|
||||
import { describe, it, expect, beforeEach } from 'vitest';
|
||||
import ChallengeTranscript from './challenge-transcript';
|
||||
|
||||
const baseProps = {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
import { runSaga } from 'redux-saga';
|
||||
import { describe, test, it, expect, beforeEach, vi, Mock } from 'vitest';
|
||||
import { render } from '../../../../utils/test-utils';
|
||||
|
||||
import { getCompletedPercentage } from '../../../utils/get-completion-percentage';
|
||||
@@ -15,21 +16,21 @@ import {
|
||||
} from '../redux/selectors';
|
||||
import { buildChallenge, getTestRunner } from '../utils/build';
|
||||
import CompletionModal, { combineFileData } from './completion-modal';
|
||||
jest.mock('../../../analytics');
|
||||
jest.mock('../../../utils/fire-confetti');
|
||||
jest.mock('../../../components/Progress');
|
||||
jest.mock('../redux/selectors');
|
||||
jest.mock('../utils/build');
|
||||
const mockFireConfetti = fireConfetti as jest.Mock;
|
||||
const mockTestRunner = jest.fn().mockReturnValue({ pass: true });
|
||||
const mockBuildEnabledSelector = isBuildEnabledSelector as jest.Mock;
|
||||
const mockChallengeTestsSelector = challengeTestsSelector as jest.Mock;
|
||||
const mockChallengeMetaSelector = challengeMetaSelector as jest.Mock;
|
||||
const mockChallengeDataSelector = challengeDataSelector as jest.Mock;
|
||||
const mockIsBlockNewlyCompletedSelector =
|
||||
isBlockNewlyCompletedSelector as jest.Mock;
|
||||
const mockBuildChallenge = buildChallenge as jest.Mock;
|
||||
const mockGetTestRunner = getTestRunner as jest.Mock;
|
||||
vi.mock('../../../analytics');
|
||||
vi.mock('../../../utils/fire-confetti');
|
||||
vi.mock('../../../components/Progress');
|
||||
vi.mock('../redux/selectors');
|
||||
vi.mock('../utils/build');
|
||||
vi.mock('../../../utils/get-words');
|
||||
const mockFireConfetti = fireConfetti as Mock;
|
||||
const mockTestRunner = vi.fn().mockReturnValue({ pass: true });
|
||||
const mockBuildEnabledSelector = isBuildEnabledSelector as Mock;
|
||||
const mockChallengeTestsSelector = challengeTestsSelector as Mock;
|
||||
const mockChallengeMetaSelector = challengeMetaSelector as Mock;
|
||||
const mockChallengeDataSelector = challengeDataSelector as Mock;
|
||||
const mockIsBlockNewlyCompletedSelector = isBlockNewlyCompletedSelector as Mock;
|
||||
const mockBuildChallenge = buildChallenge as Mock;
|
||||
const mockGetTestRunner = getTestRunner as Mock;
|
||||
mockBuildEnabledSelector.mockReturnValue(true);
|
||||
mockChallengeTestsSelector.mockReturnValue([
|
||||
{ text: 'Test 1', testString: 'mock test code' }
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import i18n from '../../../../i18n/config-for-tests';
|
||||
import { generateSearchLink } from './help-modal';
|
||||
|
||||
vi.unmock('react-i18next');
|
||||
|
||||
describe('generateSearchLink', () => {
|
||||
it("should return a link with search query containing block name and challenge title if the title includes 'step'", async () => {
|
||||
await i18n.reloadResources('en', 'intro');
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { parseBlanks } from './parse-blanks';
|
||||
|
||||
describe('parseBlanks', () => {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { TestScheduler } from 'rxjs/testing';
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import completionEpic from './completion-epic';
|
||||
import { submitChallenge, submitChallengeComplete } from './actions';
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { transformEditorLink } from '../utils';
|
||||
import { insertEditableRegions } from './create-question-epic';
|
||||
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
import { expectSaga } from 'redux-saga-test-plan';
|
||||
import { describe, it, vi } from 'vitest';
|
||||
|
||||
jest.mock('redux-saga/effects', () => ({
|
||||
...jest.requireActual('redux-saga/effects'),
|
||||
delay: jest.fn()
|
||||
}));
|
||||
vi.mock('redux-saga/effects', async importOriginal => {
|
||||
const actual = await importOriginal();
|
||||
return {
|
||||
...actual,
|
||||
delay: vi.fn()
|
||||
};
|
||||
});
|
||||
|
||||
const initialState = {
|
||||
challenge: { isBuildEnabled: true, isExecuting: false, challengeMeta: {} }
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import envData from '../../../../config/env.json';
|
||||
import { getGuideUrl } from './index';
|
||||
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
/**
|
||||
* @jest-environment node
|
||||
* @vitest-environment node
|
||||
*/
|
||||
|
||||
import { it, expect, afterEach, vi } from 'vitest';
|
||||
import { WorkerExecutor } from './worker-executor';
|
||||
|
||||
function mockWorker({ init, postMessage, terminate } = {}) {
|
||||
global.Worker = jest.fn(function () {
|
||||
global.Worker = vi.fn(function () {
|
||||
setImmediate(
|
||||
(init && init(this)) ||
|
||||
(() =>
|
||||
@@ -29,7 +30,7 @@ afterEach(() => {
|
||||
});
|
||||
|
||||
it('Worker executor should successfully execute one task', async () => {
|
||||
const terminateHandler = jest.fn();
|
||||
const terminateHandler = vi.fn();
|
||||
mockWorker({ terminate: terminateHandler });
|
||||
const testWorker = new WorkerExecutor('test');
|
||||
expect(testWorker).not.toBeUndefined();
|
||||
@@ -37,9 +38,9 @@ it('Worker executor should successfully execute one task', async () => {
|
||||
const task = testWorker.execute('test');
|
||||
expect(task).not.toBeUndefined();
|
||||
expect(task.done).not.toBeUndefined();
|
||||
const handler = jest.fn();
|
||||
const handler = vi.fn();
|
||||
task.on('done', handler);
|
||||
const errorHandler = jest.fn();
|
||||
const errorHandler = vi.fn();
|
||||
task.on('error', errorHandler);
|
||||
|
||||
await expect(task.done).resolves.toBe('test processed');
|
||||
@@ -55,20 +56,20 @@ it('Worker executor should successfully execute one task', async () => {
|
||||
});
|
||||
|
||||
it('Worker executor should successfully execute two tasks in parallel', async () => {
|
||||
const terminateHandler = jest.fn();
|
||||
const terminateHandler = vi.fn();
|
||||
mockWorker({ terminate: terminateHandler });
|
||||
const testWorker = new WorkerExecutor('test');
|
||||
|
||||
const task1 = testWorker.execute('test1');
|
||||
const handler1 = jest.fn();
|
||||
const handler1 = vi.fn();
|
||||
task1.on('done', handler1);
|
||||
const errorHandler1 = jest.fn();
|
||||
const errorHandler1 = vi.fn();
|
||||
task1.on('error', errorHandler1);
|
||||
|
||||
const task2 = testWorker.execute('test2');
|
||||
const handler2 = jest.fn();
|
||||
const handler2 = vi.fn();
|
||||
task2.on('done', handler2);
|
||||
const errorHandler2 = jest.fn();
|
||||
const errorHandler2 = vi.fn();
|
||||
task2.on('error', errorHandler2);
|
||||
|
||||
await expect(Promise.all([task1.done, task2.done])).resolves.toEqual([
|
||||
@@ -103,7 +104,7 @@ it('Worker executor should successfully execute 3 tasks in parallel and use two
|
||||
});
|
||||
|
||||
it('Worker executor should successfully execute 3 tasks, use 3 workers and terminate each worker', async () => {
|
||||
const terminateHandler = jest.fn();
|
||||
const terminateHandler = vi.fn();
|
||||
mockWorker({ terminate: terminateHandler });
|
||||
const testWorker = new WorkerExecutor('test', { terminateWorker: true });
|
||||
|
||||
@@ -156,7 +157,7 @@ it('Worker executor should reject task', async () => {
|
||||
const testWorker = new WorkerExecutor('test');
|
||||
|
||||
const task = testWorker.execute('test');
|
||||
const errorHandler = jest.fn();
|
||||
const errorHandler = vi.fn();
|
||||
task.on('error', errorHandler);
|
||||
await expect(task.done).rejects.toBe(error.message);
|
||||
|
||||
@@ -184,11 +185,11 @@ it('Worker executor should emit LOG events', async () => {
|
||||
const testWorker = new WorkerExecutor('test');
|
||||
|
||||
const task = testWorker.execute('test');
|
||||
const handler = jest.fn();
|
||||
const handler = vi.fn();
|
||||
task.on('done', handler);
|
||||
const errorHandler = jest.fn();
|
||||
const errorHandler = vi.fn();
|
||||
task.on('error', errorHandler);
|
||||
const logHandler = jest.fn();
|
||||
const logHandler = vi.fn();
|
||||
task.on('LOG', logHandler);
|
||||
|
||||
await expect(task.done).resolves.toBe('test processed');
|
||||
@@ -204,7 +205,7 @@ it('Worker executor should emit LOG events', async () => {
|
||||
});
|
||||
|
||||
it('Worker executor should reject task on timeout', async () => {
|
||||
const terminateHandler = jest.fn();
|
||||
const terminateHandler = vi.fn();
|
||||
mockWorker({
|
||||
postMessage: () => {},
|
||||
terminate: terminateHandler
|
||||
@@ -212,7 +213,7 @@ it('Worker executor should reject task on timeout', async () => {
|
||||
const testWorker = new WorkerExecutor('test');
|
||||
|
||||
const task = testWorker.execute('test', 0);
|
||||
const errorHandler = jest.fn();
|
||||
const errorHandler = vi.fn();
|
||||
task.on('error', errorHandler);
|
||||
await expect(task.done).rejects.toBe('timeout');
|
||||
|
||||
@@ -239,7 +240,7 @@ it('Task should only emit handler once', () => {
|
||||
mockWorker();
|
||||
const testWorker = new WorkerExecutor('test');
|
||||
const task = testWorker.execute('test');
|
||||
const handler = jest.fn();
|
||||
const handler = vi.fn();
|
||||
task.once('testOnce', handler);
|
||||
|
||||
task.emit('testOnce', handler);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`<BlockIntros /> matches snapshot 1`] = `
|
||||
exports[`<BlockIntros /> > matches snapshot 1`] = `
|
||||
<div
|
||||
className="block-description"
|
||||
>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
import renderer from 'react-test-renderer';
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import BlockIntros from './block-intros';
|
||||
|
||||
const arrString = ['first paragraph', 'second paragraph'];
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import React from 'react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { Provider } from 'react-redux';
|
||||
import { describe, it, expect, afterEach, vi, Mock } from 'vitest';
|
||||
import type { TFunction } from 'i18next';
|
||||
import { SuperBlocks } from '../../../../../shared/config/curriculum';
|
||||
import { createStore } from '../../../redux/create-store';
|
||||
import {
|
||||
ChallengeFiles,
|
||||
PrerequisiteChallenge,
|
||||
@@ -14,18 +14,13 @@ import {
|
||||
} from '../../../redux/prop-types';
|
||||
import { isAuditedSuperBlock } from '../../../../../shared/utils/is-audited';
|
||||
import { BlockLayouts, BlockTypes } from '../../../../../shared/config/blocks';
|
||||
import Block from './block';
|
||||
import { Block } from './block';
|
||||
|
||||
jest.mock('../../../../../shared/utils/is-audited', () => ({
|
||||
isAuditedSuperBlock: jest.fn().mockReturnValueOnce(true)
|
||||
vi.mock('../../../../../shared/utils/is-audited', () => ({
|
||||
isAuditedSuperBlock: vi.fn().mockReturnValueOnce(true)
|
||||
}));
|
||||
|
||||
jest.mock('../redux', () => ({
|
||||
makeExpandedBlockSelector: jest.fn(() => jest.fn(() => true)),
|
||||
completedChallengesSelector: jest.fn(() => [
|
||||
{ id: 'mockId', title: 'mockTitle' }
|
||||
])
|
||||
}));
|
||||
vi.mock('../../../utils/get-words');
|
||||
|
||||
const defaultProps = {
|
||||
block: 'test-block',
|
||||
@@ -91,46 +86,34 @@ const defaultProps = {
|
||||
],
|
||||
completedChallengeIds: ['testchallengeIds'],
|
||||
isExpanded: true,
|
||||
t: jest.fn((key: string) => [key]),
|
||||
t: vi.fn((key: string) => [key]) as unknown as TFunction,
|
||||
superBlock: SuperBlocks.FullStackDeveloper,
|
||||
toggleBlock: jest.fn()
|
||||
toggleBlock: vi.fn()
|
||||
};
|
||||
|
||||
describe('<Block />', () => {
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('The "Help us translate" badge does not appear on any English blocks', () => {
|
||||
render(
|
||||
<Provider store={createStore()}>
|
||||
<Block {...defaultProps} />
|
||||
</Provider>
|
||||
);
|
||||
render(<Block {...defaultProps} />);
|
||||
expect(
|
||||
screen.queryByText(/misc.translation-pending/)
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it(`The "Help us translate" badge does not appear on any i18n blocks when the superblock is audited`, () => {
|
||||
(isAuditedSuperBlock as jest.Mock).mockReturnValue(true);
|
||||
render(
|
||||
<Provider store={createStore()}>
|
||||
<Block {...defaultProps} />
|
||||
</Provider>
|
||||
);
|
||||
(isAuditedSuperBlock as Mock).mockReturnValue(true);
|
||||
render(<Block {...defaultProps} />);
|
||||
expect(
|
||||
screen.queryByText(/misc.translation-pending/)
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it(`The "Help us translate" badge does appear on i18n blocks when the superblock is not audited`, () => {
|
||||
(isAuditedSuperBlock as jest.Mock).mockReturnValue(false);
|
||||
render(
|
||||
<Provider store={createStore()}>
|
||||
<Block {...defaultProps} />
|
||||
</Provider>
|
||||
);
|
||||
(isAuditedSuperBlock as Mock).mockReturnValue(false);
|
||||
render(<Block {...defaultProps} />);
|
||||
expect(screen.getByText(/misc.translation-pending/)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -69,7 +69,7 @@ interface BlockProps {
|
||||
toggleBlock: typeof toggleBlock;
|
||||
}
|
||||
|
||||
class Block extends Component<BlockProps> {
|
||||
export class Block extends Component<BlockProps> {
|
||||
static displayName: string;
|
||||
constructor(props: BlockProps) {
|
||||
super(props);
|
||||
|
||||
7
client/src/utils/__mocks__/get-words.ts
Normal file
7
client/src/utils/__mocks__/get-words.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { vi } from 'vitest';
|
||||
|
||||
export const randomQuote = vi.fn(() => ({
|
||||
quote: 'Test quote',
|
||||
author: 'Test author'
|
||||
}));
|
||||
export const randomCompliment = vi.fn(() => 'Great job!');
|
||||
@@ -1,3 +1,4 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { stringifyDonationEvents } from './analytics-strings';
|
||||
|
||||
describe('Analytics donation strings', () => {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { createTypes, createAsyncTypes } from './create-types';
|
||||
|
||||
describe('Create types utility (createTypes)', () => {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { format } from './format';
|
||||
|
||||
function simpleFun() {
|
||||
|
||||
@@ -1,30 +1,8 @@
|
||||
// @vitest-environment jsdom
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { getUUID } from './growthbook-cookie';
|
||||
|
||||
describe('getUUID', () => {
|
||||
let originalCookie: string;
|
||||
|
||||
beforeEach(() => {
|
||||
global.crypto = {
|
||||
...global.crypto,
|
||||
randomUUID: () => '123e4567-e89b-12d3-a456-426614174000'
|
||||
};
|
||||
|
||||
// Save original cookie
|
||||
originalCookie = document.cookie;
|
||||
|
||||
// Clear the cookie before each test
|
||||
document.cookie.split(';').forEach(c => {
|
||||
document.cookie = c
|
||||
.replace(/^ +/, '')
|
||||
.replace(/=.*/, '=;expires=' + new Date().toUTCString() + ';path=/');
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
// Restore original cookie
|
||||
document.cookie = originalCookie;
|
||||
});
|
||||
|
||||
it('should generate a new UUID if none exists', () => {
|
||||
const uuid = getUUID();
|
||||
expect(uuid).toBeDefined();
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { isObject } from 'lodash-es';
|
||||
|
||||
import {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { isContained } from './is-contained';
|
||||
|
||||
describe('client/src isContained', () => {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { isLanding } from './path-parsers';
|
||||
|
||||
const pathnames = {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { replaceAppleQuotes } from './replace-apple-quotes';
|
||||
|
||||
describe('replaceAppleQuotes()', () => {
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
// @vitest-environment jsdom
|
||||
/* eslint-disable @typescript-eslint/unbound-method */
|
||||
import { describe, test, expect, beforeEach, afterEach, vi } from 'vitest';
|
||||
import {
|
||||
CURRENT_COUNT_KEY,
|
||||
SAVED_COUNT_KEY,
|
||||
@@ -17,24 +19,24 @@ describe('Session Storage', () => {
|
||||
const mockStorage: Storage = {
|
||||
length: 0,
|
||||
|
||||
clear: jest.fn(() => {
|
||||
clear: vi.fn(() => {
|
||||
store = {};
|
||||
}),
|
||||
|
||||
getItem: jest.fn((key: string) => {
|
||||
getItem: vi.fn((key: string) => {
|
||||
return store[key] || null;
|
||||
}),
|
||||
|
||||
key: jest.fn((index: number) => {
|
||||
key: vi.fn((index: number) => {
|
||||
const keys = Object.keys(store);
|
||||
return keys[index] || null;
|
||||
}),
|
||||
|
||||
removeItem: jest.fn((key: string) => {
|
||||
removeItem: vi.fn((key: string) => {
|
||||
delete store[key];
|
||||
}),
|
||||
|
||||
setItem: jest.fn((key: string, value: string) => {
|
||||
setItem: vi.fn((key: string, value: string) => {
|
||||
store[key] = value;
|
||||
})
|
||||
};
|
||||
@@ -54,16 +56,16 @@ describe('Session Storage', () => {
|
||||
|
||||
afterEach(() => {
|
||||
sessionStorage.clear();
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('getSessionChallengeData', () => {
|
||||
describe('countSinceSave', () => {
|
||||
it('is not included if nothing has been saved', () => {
|
||||
test('is not included if nothing has been saved', () => {
|
||||
expect(getSessionChallengeData()).not.toHaveProperty('countSinceSave');
|
||||
});
|
||||
|
||||
it('is included if the count has been saved', () => {
|
||||
test('is included if the count has been saved', () => {
|
||||
sessionStorage.setItem(SAVED_COUNT_KEY, '7');
|
||||
sessionStorage.setItem(CURRENT_COUNT_KEY, '10');
|
||||
expect(getSessionChallengeData()).toMatchObject({
|
||||
@@ -73,13 +75,13 @@ describe('Session Storage', () => {
|
||||
});
|
||||
|
||||
describe('currentCount', () => {
|
||||
it('defaults to 0 if no challenges have been completed', () => {
|
||||
test('defaults to 0 if no challenges have been completed', () => {
|
||||
expect(getSessionChallengeData()).toMatchObject({
|
||||
currentCount: 0
|
||||
});
|
||||
});
|
||||
|
||||
it('returns the stored number if it exists', () => {
|
||||
test('returns the stored number if test exists', () => {
|
||||
sessionStorage.setItem(CURRENT_COUNT_KEY, '5');
|
||||
expect(getSessionChallengeData()).toMatchObject({
|
||||
currentCount: 5
|
||||
@@ -88,13 +90,13 @@ describe('Session Storage', () => {
|
||||
});
|
||||
|
||||
describe('isSaved', () => {
|
||||
it('is false if we haved saved the count', () => {
|
||||
test('is false if we haved saved the count', () => {
|
||||
expect(getSessionChallengeData()).toMatchObject({
|
||||
isSaved: false
|
||||
});
|
||||
});
|
||||
|
||||
it('is true if we have saved something', () => {
|
||||
test('is true if we have saved something', () => {
|
||||
sessionStorage.setItem(SAVED_COUNT_KEY, '7');
|
||||
expect(getSessionChallengeData()).toMatchObject({
|
||||
isSaved: true
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import {
|
||||
bothLinks,
|
||||
invalidGithubLink,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { withPrefix } from 'gatsby';
|
||||
import { describe, it, expect } from 'vitest';
|
||||
|
||||
import toLearnPath from './to-learn-path';
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { describe, test, expect } from 'vitest';
|
||||
|
||||
import { clientLocale } from '../config/env.json';
|
||||
import {
|
||||
convertToLocalizedString,
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
"i18n/**/*",
|
||||
"plugins/**/*",
|
||||
"src/**/*",
|
||||
"__mocks__/**/*",
|
||||
"utils/**/*",
|
||||
"tools/**/*",
|
||||
"config/**/*"
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import ShallowRenderer from 'react-test-renderer/shallow';
|
||||
import { describe, test, expect, vi } from 'vitest';
|
||||
|
||||
import FourOhFourPage from '../../src/pages/404';
|
||||
import Certification from '../../src/pages/certification';
|
||||
@@ -9,7 +10,9 @@ import Learn from '../../src/pages/learn';
|
||||
import { createStore } from '../../src/redux/create-store';
|
||||
import layoutSelector from './layout-selector';
|
||||
|
||||
jest.mock('../../src/analytics');
|
||||
vi.mock('../../src/analytics');
|
||||
|
||||
vi.mock('../../src/utils/get-words');
|
||||
|
||||
const store = createStore();
|
||||
|
||||
@@ -60,55 +63,60 @@ const challengePageContext = {
|
||||
}
|
||||
};
|
||||
|
||||
test('Challenges should have DefaultLayout and no footer', () => {
|
||||
const challengePath =
|
||||
'/learn/responsive-web-design/basic-html-and-html5/say-hello-to-html-elements';
|
||||
const componentObj = getComponentNameAndProps(
|
||||
Learn,
|
||||
challengePath,
|
||||
challengePageContext
|
||||
);
|
||||
expect(componentObj.name).toEqual('DefaultLayout');
|
||||
expect(componentObj.props.showFooter).toEqual(false);
|
||||
});
|
||||
describe('Layout selector', () => {
|
||||
test('Challenges should have DefaultLayout and no footer', () => {
|
||||
const challengePath =
|
||||
'/learn/responsive-web-design/basic-html-and-html5/say-hello-to-html-elements';
|
||||
const componentObj = getComponentNameAndProps(
|
||||
Learn,
|
||||
challengePath,
|
||||
challengePageContext
|
||||
);
|
||||
expect(componentObj.name).toEqual('DefaultLayout');
|
||||
expect(componentObj.props.showFooter).toEqual(false);
|
||||
});
|
||||
|
||||
test('SuperBlock path should have DefaultLayout and footer', () => {
|
||||
const superBlockPath = '/learn/responsive-web-design/';
|
||||
const componentObj = getComponentNameAndProps(Learn, superBlockPath);
|
||||
expect(componentObj.name).toEqual('DefaultLayout');
|
||||
expect(componentObj.props.showFooter).toEqual(true);
|
||||
});
|
||||
test('SuperBlock path should have DefaultLayout and footer', () => {
|
||||
const superBlockPath = '/learn/responsive-web-design/';
|
||||
const componentObj = getComponentNameAndProps(Learn, superBlockPath);
|
||||
expect(componentObj.name).toEqual('DefaultLayout');
|
||||
expect(componentObj.props.showFooter).toEqual(true);
|
||||
});
|
||||
|
||||
test('i18n challenge path should have DefaultLayout and no footer', () => {
|
||||
const challengePath =
|
||||
'espanol/learn/responsive-web-design/basic-html-and-html5/say-hello-to-html-elements/';
|
||||
const componentObj = getComponentNameAndProps(
|
||||
Learn,
|
||||
challengePath,
|
||||
challengePageContext
|
||||
);
|
||||
expect(componentObj.name).toEqual('DefaultLayout');
|
||||
expect(componentObj.props.showFooter).toEqual(false);
|
||||
});
|
||||
test('i18n challenge path should have DefaultLayout and no footer', () => {
|
||||
const challengePath =
|
||||
'espanol/learn/responsive-web-design/basic-html-and-html5/say-hello-to-html-elements/';
|
||||
const componentObj = getComponentNameAndProps(
|
||||
Learn,
|
||||
challengePath,
|
||||
challengePageContext
|
||||
);
|
||||
expect(componentObj.name).toEqual('DefaultLayout');
|
||||
expect(componentObj.props.showFooter).toEqual(false);
|
||||
});
|
||||
|
||||
test('i18n superBlock path should have DefaultLayout and footer', () => {
|
||||
const superBlockPath = '/learn/responsive-web-design/';
|
||||
const componentObj = getComponentNameAndProps(Learn, superBlockPath);
|
||||
expect(componentObj.name).toEqual('DefaultLayout');
|
||||
expect(componentObj.props.showFooter).toEqual(true);
|
||||
});
|
||||
test('i18n superBlock path should have DefaultLayout and footer', () => {
|
||||
const superBlockPath = '/learn/responsive-web-design/';
|
||||
const componentObj = getComponentNameAndProps(Learn, superBlockPath);
|
||||
expect(componentObj.name).toEqual('DefaultLayout');
|
||||
expect(componentObj.props.showFooter).toEqual(true);
|
||||
});
|
||||
|
||||
test('404 page should have DefaultLayout and footer', () => {
|
||||
const challengePath =
|
||||
'/espanol/learn/responsive-web-design/basic-html-and-html5/say-hello-to-html-elements/';
|
||||
const componentObj = getComponentNameAndProps(FourOhFourPage, challengePath);
|
||||
expect(componentObj.name).toEqual('DefaultLayout');
|
||||
expect(componentObj.props.showFooter).toEqual(true);
|
||||
});
|
||||
test('404 page should have DefaultLayout and footer', () => {
|
||||
const challengePath =
|
||||
'/espanol/learn/responsive-web-design/basic-html-and-html5/say-hello-to-html-elements/';
|
||||
const componentObj = getComponentNameAndProps(
|
||||
FourOhFourPage,
|
||||
challengePath
|
||||
);
|
||||
expect(componentObj.name).toEqual('DefaultLayout');
|
||||
expect(componentObj.props.showFooter).toEqual(true);
|
||||
});
|
||||
|
||||
test('Certification path should have CertificationLayout', () => {
|
||||
const challengePath =
|
||||
'/certification/mot01/javascript-algorithms-and-data-structures/';
|
||||
const componentObj = getComponentNameAndProps(Certification, challengePath);
|
||||
expect(componentObj.name).toEqual('CertificationLayout');
|
||||
test('Certification path should have CertificationLayout', () => {
|
||||
const challengePath =
|
||||
'/certification/mot01/javascript-algorithms-and-data-structures/';
|
||||
const componentObj = getComponentNameAndProps(Certification, challengePath);
|
||||
expect(componentObj.name).toEqual('CertificationLayout');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { challengeFiles } from './__fixtures__/challenges';
|
||||
import { sortChallengeFiles } from './sort-challengefiles';
|
||||
|
||||
|
||||
13
client/vitest-setup.js
Normal file
13
client/vitest-setup.js
Normal file
@@ -0,0 +1,13 @@
|
||||
import { vi, afterEach } from 'vitest';
|
||||
import '@testing-library/jest-dom/vitest';
|
||||
import { cleanup } from '@testing-library/react';
|
||||
|
||||
vi.mock('react-spinkit');
|
||||
vi.mock('gatsby');
|
||||
vi.mock('react-i18next');
|
||||
|
||||
afterEach(() => {
|
||||
// Vitest doesn't automatically call cleanup. Without it the rendered
|
||||
// components are never removed and so tests are not independent.
|
||||
cleanup();
|
||||
});
|
||||
27
client/vitest.config.js
Normal file
27
client/vitest.config.js
Normal file
@@ -0,0 +1,27 @@
|
||||
/// <reference types="vitest/config" />
|
||||
import { defineConfig } from 'vitest/config';
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
setupFiles: 'vitest-setup.js',
|
||||
exclude: 'node_modules',
|
||||
projects: [
|
||||
{
|
||||
extends: true,
|
||||
test: {
|
||||
include: '**/*.test.{jsx,tsx}',
|
||||
name: 'react',
|
||||
environment: 'jsdom'
|
||||
}
|
||||
},
|
||||
{
|
||||
extends: true,
|
||||
test: {
|
||||
include: '**/*.test.{js,ts}',
|
||||
name: 'js',
|
||||
environment: 'node'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
@@ -6,15 +6,10 @@ module.exports = {
|
||||
'tools/challenge-helper-scripts/',
|
||||
'tools/challenge-parser/',
|
||||
'tools/scripts/build/',
|
||||
'curriculum'
|
||||
'curriculum',
|
||||
'client'
|
||||
],
|
||||
moduleNameMapper: {
|
||||
'\\.(jpg|jpeg|png|svg|woff|woff2)$':
|
||||
'<rootDir>/client/src/__mocks__/file-mock.ts',
|
||||
// Plain CSS - match css files that don't end with
|
||||
// '.module.css' https://regex101.com/r/VzwrKH/4
|
||||
'^(?!.*\\.module\\.css$).*\\.css$':
|
||||
'<rootDir>/client/src/__mocks__/style-mock.ts',
|
||||
// CSS Modules - match files that end with 'module.css'
|
||||
'\\.module\\.css$': 'identity-obj-proxy',
|
||||
'^lodash-es$': 'lodash'
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
"test:curriculum:content": "cd ./curriculum && pnpm test",
|
||||
"test:curriculum:tooling": "cd ./curriculum && pnpm vitest run",
|
||||
"test-curriculum-full-output": "cd ./curriculum && pnpm run test:full-output",
|
||||
"test-client": "jest client",
|
||||
"test:client": "cd ./client && pnpm test run",
|
||||
"test-config": "jest config",
|
||||
"test-tools": "jest tools",
|
||||
"test-utils": "jest utils",
|
||||
|
||||
523
pnpm-lock.yaml
generated
523
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -13,3 +13,9 @@ packages:
|
||||
- 'tools/scripts/build'
|
||||
- 'tools/scripts/seed'
|
||||
- 'tools/scripts/seed-exams'
|
||||
|
||||
packageExtensions:
|
||||
'@testing-library/jest-dom':
|
||||
peerDependencies:
|
||||
jest: '*'
|
||||
vitest: '*'
|
||||
|
||||
Reference in New Issue
Block a user