chore: remove jest -> cypress for unit tests

This commit is contained in:
Divlo
2022-08-23 21:51:20 +02:00
parent d8cedd7b77
commit 7ad3d226dc
87 changed files with 2668 additions and 7876 deletions

View File

@ -0,0 +1,18 @@
import { isStringWithOnlyOneEmoji } from '../../../../components/Emoji/isStringWithOnlyOneEmoji'
describe('components/Emoji/isStringWithOnlyOneEmoji', () => {
it('returns true with a string with only one emoji', () => {
expect(isStringWithOnlyOneEmoji(':wave:')).equal(true)
expect(isStringWithOnlyOneEmoji(':smile:')).equal(true)
})
it('returns false with a string with multiple emoji or with text', () => {
expect(isStringWithOnlyOneEmoji(':wave: :smile:')).equal(false)
expect(isStringWithOnlyOneEmoji(':wave: some text')).equal(false)
expect(isStringWithOnlyOneEmoji('some text :wave:')).equal(false)
})
it('returns false with a string without emoji', () => {
expect(isStringWithOnlyOneEmoji('some text')).equal(false)
})
})

View File

@ -0,0 +1,16 @@
import { Footer } from '../../../components/Footer'
describe('<Footer />', () => {
it('should render with appropriate link tag version', () => {
const version = '1.0.0'
cy.mount(<Footer version={version} />)
cy.contains('Thream')
.get('[data-cy=version-link-website]')
.should('have.text', `website v${version}`)
.should(
'have.attr',
'href',
`https://github.com/Thream/website/releases/tag/v${version}`
)
})
})

View File

@ -0,0 +1,35 @@
import { FormState } from '../../../../components/design/FormState'
describe('<FormState />', () => {
it('should return nothing if the state is idle', () => {
cy.mount(<FormState state='idle' />)
.get('[data-cy-root]')
.should('be.empty')
})
it('should return nothing if the message is null', () => {
cy.mount(<FormState state='error' />)
.get('[data-cy-root]')
.should('be.empty')
})
it('should render the <Loader /> if state is loading', () => {
cy.mount(<FormState state='loading' />)
.get('[data-cy=loader]')
.should('be.visible')
})
it('should render the success message if state is success', () => {
const message = 'Success Message'
cy.mount(<FormState state='success' message={message} id='success' />)
.get('#success')
.contains(message)
})
it('should render the error message if state is error', () => {
const message = 'Error Message'
cy.mount(<FormState state='error' message={message} id='error' />)
.get('#error')
.contains(message)
})
})

View File

@ -0,0 +1,48 @@
import { Input, getInputType } from '../../../../components/design/Input'
describe('<Input />', () => {
it('should render the label', () => {
const labelContent = 'label content'
cy.mount(<Input label={labelContent} />)
.get('label')
.should('have.text', labelContent)
})
it('should not render forgot password link', () => {
cy.mount(<Input type='text' label='content' showForgotPassword />)
.get('[data-cy=forgot-password-link]')
.should('not.exist')
})
it('should render forgot password link', () => {
cy.mount(<Input type='password' label='content' showForgotPassword />)
.get('[data-cy=forgot-password-link]')
.should('exist')
})
it('should not render the eye icon if the input is not of type "password"', () => {
cy.mount(<Input type='text' label='content' />)
.get('[data-cy=password-eye]')
.should('not.exist')
})
it('should handlePassword with eye icon', async () => {
cy.mount(<Input type='password' label='content' />)
.get('input')
.should('have.attr', 'type', 'password')
.get('[data-cy=password-eye]')
.click()
.get('input')
.should('have.attr', 'type', 'text')
})
describe('getInputType', () => {
it('should return `text`', () => {
expect(getInputType('password')).equal('text')
})
it('should return `password`', () => {
expect(getInputType('text')).equal('password')
})
})
})

View File

@ -0,0 +1,71 @@
import type { ErrorObject } from 'ajv'
import { getErrorTranslationKey } from '../../../../hooks/useForm/getErrorTranslationKey'
const errorObject: ErrorObject = {
instancePath: '/path',
keyword: 'keyword',
params: {},
schemaPath: '/path'
}
describe('hooks/useForm/getErrorTranslationKey', () => {
it('returns `errors:invalid` with unknown keyword', () => {
expect(
getErrorTranslationKey({
...errorObject,
keyword: 'unknownkeyword'
})
).equal('errors:invalid')
})
it('returns `errors:invalid` with format != email', () => {
expect(
getErrorTranslationKey({
...errorObject,
keyword: 'format',
params: { format: 'email' }
})
).equal('errors:email')
})
it('returns `errors:email` with format = email', () => {
expect(
getErrorTranslationKey({
...errorObject,
keyword: 'format',
params: { format: 'email' }
})
).equal('errors:email')
})
it('returns `errors:required` with minLength and limit = 1', () => {
expect(
getErrorTranslationKey({
...errorObject,
keyword: 'minLength',
params: { limit: 1 }
})
).equal('errors:required')
})
it('returns `errors:minLength` with minLength and limit > 1', () => {
expect(
getErrorTranslationKey({
...errorObject,
keyword: 'minLength',
params: { limit: 5 }
})
).equal('errors:minLength')
})
it('returns `errors:maxLength` with maxLength', () => {
expect(
getErrorTranslationKey({
...errorObject,
keyword: 'maxLength',
params: { limit: 5 }
})
).equal('errors:maxLength')
})
})

View File

@ -0,0 +1,22 @@
import { Type } from '@sinclair/typebox'
import { handleCheckboxBoolean } from '../../../../hooks/useForm/handleCheckboxBoolean'
const schema = Type.Object({
myBoolean: Type.Boolean(),
myString: Type.String()
})
describe('hooks/useForm/handleCheckboxBoolean', () => {
it('should convert all checkbox property to boolean', () => {
const object = {
myBoolean: 'on',
myString: 'on'
}
const result = handleCheckboxBoolean(object, schema)
expect(result).deep.equal({
myBoolean: true,
myString: 'on'
})
})
})

View File

@ -0,0 +1,20 @@
import { replaceEmptyStringInObjectToNull } from '../../../../hooks/useForm/replaceEmptyStringInObjectToNull'
describe('hooks/useForm/replaceEmptyStringInObjectToNull', () => {
it('should replace empty string in object to null except for required properties', () => {
expect(
replaceEmptyStringInObjectToNull(
{
foo: '',
bar: 'bar',
baz: ''
},
['baz']
)
).deep.equal({
foo: null,
bar: 'bar',
baz: ''
})
})
})

View File

@ -0,0 +1,10 @@
import { capitalize } from '../../../../tools/utils/capitalize'
describe('tools/utils/capitalize', () => {
it('should capitalize the first letter of a string', () => {
expect(capitalize('hello')).equal('Hello')
expect(capitalize('HeLlo')).equal('HeLlo')
expect(capitalize('member(s)')).equal('Member(s)')
expect(capitalize('Member(s)')).equal('Member(s)')
})
})

View File

@ -49,3 +49,5 @@ describe('Common > Header', () => {
})
})
})
export {}

View File

@ -45,3 +45,5 @@ describe('Common > application/authentication', () => {
}
})
})
export {}

View File

@ -7,3 +7,5 @@ describe('Page > /404', () => {
cy.get('[data-cy=status-code]').contains('404')
})
})
export {}

View File

@ -7,3 +7,5 @@ describe('Page > /500', () => {
cy.get('[data-cy=status-code]').contains('500')
})
})
export {}

View File

@ -267,3 +267,5 @@ describe('Pages > /application/[guildId]/[channelId]', () => {
.should('eq', '/404')
})
})
export {}

View File

@ -132,3 +132,5 @@ describe('Pages > /application/[guildId]/[channelId]/settings', () => {
.should('eq', '/404')
})
})
export {}

View File

@ -72,3 +72,5 @@ describe('Pages > /application/[guildId]/channels/create', () => {
.should('eq', '/404')
})
})
export {}

View File

@ -102,3 +102,5 @@ describe('Pages > /application/[guildId]/settings', () => {
.should('eq', '/404')
})
})
export {}

View File

@ -42,3 +42,5 @@ describe('Pages > /application/guilds/create', () => {
cy.get('#message').should('have.text', 'Error: Internal Server Error.')
})
})
export {}

View File

@ -75,6 +75,8 @@ describe('Pages > /application/guilds/join', () => {
)
cy.visit('/application/guilds/join')
cy.get('.guilds-public-list').children().should('have.length', 1)
cy.get('[data-testid=progress-spinner]').should('be.visible')
cy.get('[data-cy=progress-spinner]').should('be.visible')
})
})
export {}

View File

@ -30,3 +30,5 @@ describe('Pages > /application', () => {
.should('eq', '/application/guilds/join')
})
})
export {}

View File

@ -33,3 +33,5 @@ describe('Pages > /application/users/[userId]', () => {
.should('eq', '/404')
})
})
export {}

View File

@ -35,3 +35,5 @@ describe('Pages > /authentication/forgot-password', () => {
)
})
})
export {}

View File

@ -46,3 +46,5 @@ describe('Pages > /authentication/reset-password', () => {
)
})
})
export {}

View File

@ -53,3 +53,5 @@ describe('Pages > /authentication/signin', () => {
cy.get('#error-password').should('not.exist')
})
})
export {}

View File

@ -83,3 +83,5 @@ describe('Pages > /authentication/signup', () => {
)
})
})
export {}

View File

@ -10,3 +10,5 @@ describe('Page > /', () => {
.should('eq', '/authentication/signup')
})
})
export {}

View File

@ -1,66 +0,0 @@
import path from 'node:path'
import { getLocal } from 'mockttp'
import { API_DEFAULT_PORT } from '../../tools/api'
/// <reference types="cypress" />
/**
* @typedef {import('../fixtures/handler').Method} Method
*/
/** @type {import('mockttp').Mockttp | null} */
let server = null
const UPLOADS_FIXTURES_DIRECTORY = path.join(
process.cwd(),
'cypress',
'fixtures',
'uploads'
)
/**
* @type {Cypress.PluginConfig}
*/
module.exports = (on, config) => {
on('task', {
/**
* @param {import('../fixtures/handler').Handlers} handlers
*/
async startMockServer(handlers) {
server = getLocal({
cors: true
})
await server.start(API_DEFAULT_PORT)
for (const handler of handlers) {
const { isFile = false } = handler.response
const method = /** @type {Lowercase<Method>} */ (
handler.method.toLowerCase()
)
if (isFile) {
await server[method](handler.url).thenFromFile(
handler.response.statusCode,
path.join(UPLOADS_FIXTURES_DIRECTORY, ...handler.response.body)
)
} else {
await server[method](handler.url).thenJson(
handler.response.statusCode,
handler.response.body
)
}
}
return null
},
async stopMockServer() {
if (server != null) {
await server.stop()
server = null
}
return null
}
})
return config
}

View File

@ -0,0 +1,3 @@
/// <reference types="cypress" />
export {}

View File

@ -0,0 +1,14 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<title>Components App</title>
<!-- Used by Next.js to inject CSS. -->
<div id="__next_css__DO_NOT_USE__"></div>
</head>
<body>
<div data-cy-root></div>
</body>
</html>

View File

@ -0,0 +1,14 @@
import { mount } from 'cypress/react'
import './commands'
import '../../styles/global.css'
declare global {
namespace Cypress {
interface Chainable {
mount: typeof mount
}
}
}
Cypress.Commands.add('mount', mount)

View File

@ -1,9 +0,0 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"noEmit": true,
"types": ["cypress"],
"isolatedModules": false
},
"include": ["../node_modules/cypress", "./**/*.ts"]
}