feat: design applications and first api calls
Co-authored-by: Walid <87608619+WalidKorchi@users.noreply.github.com>
This commit is contained in:
18
cypress/fixtures/handler.ts
Normal file
18
cypress/fixtures/handler.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { getUsersCurrentHandler } from './users/current/get'
|
||||
import { postUsersRefreshTokenHandler } from './users/refresh-token/post'
|
||||
|
||||
export interface Handler {
|
||||
method: 'GET' | 'POST' | 'PUT' | 'DELETE'
|
||||
url: string
|
||||
response: {
|
||||
body: any
|
||||
statusCode: number
|
||||
}
|
||||
}
|
||||
|
||||
export type Handlers = Handler[]
|
||||
|
||||
export const authenticationHandlers = [
|
||||
getUsersCurrentHandler,
|
||||
postUsersRefreshTokenHandler
|
||||
]
|
19
cypress/fixtures/users/current/get.ts
Normal file
19
cypress/fixtures/users/current/get.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { Handler } from '../../handler'
|
||||
|
||||
import { user, userSettings } from '../user'
|
||||
|
||||
export const getUsersCurrentHandler: Handler = {
|
||||
method: 'GET',
|
||||
url: '/users/current',
|
||||
response: {
|
||||
statusCode: 200,
|
||||
body: {
|
||||
user: {
|
||||
...user,
|
||||
settings: userSettings,
|
||||
currentStrategy: 'local',
|
||||
strategies: ['local']
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
14
cypress/fixtures/users/refresh-token/post.ts
Normal file
14
cypress/fixtures/users/refresh-token/post.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { Handler } from '../../handler'
|
||||
|
||||
export const postUsersRefreshTokenHandler: Handler = {
|
||||
method: 'POST',
|
||||
url: '/users/refresh-token',
|
||||
response: {
|
||||
statusCode: 200,
|
||||
body: {
|
||||
accessToken: 'access-token',
|
||||
expiresIn: 3600000,
|
||||
type: 'Bearer'
|
||||
}
|
||||
}
|
||||
}
|
10
cypress/fixtures/users/reset-password/post.ts
Normal file
10
cypress/fixtures/users/reset-password/post.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { Handler } from '../../handler'
|
||||
|
||||
export const postUsersResetPasswordHandler: Handler = {
|
||||
method: 'POST',
|
||||
url: '/users/reset-password',
|
||||
response: {
|
||||
statusCode: 200,
|
||||
body: 'Password-reset request successful, please check your emails!'
|
||||
}
|
||||
}
|
23
cypress/fixtures/users/reset-password/put.ts
Normal file
23
cypress/fixtures/users/reset-password/put.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { Handler } from '../../handler'
|
||||
|
||||
export const putUsersResetPasswordHandler: Handler = {
|
||||
method: 'PUT',
|
||||
url: '/users/reset-password',
|
||||
response: {
|
||||
statusCode: 200,
|
||||
body: 'The new password has been saved!'
|
||||
}
|
||||
}
|
||||
|
||||
export const putUsersResetPasswordInvalidTemporaryTokenHandler: Handler = {
|
||||
method: 'PUT',
|
||||
url: '/users/reset-password',
|
||||
response: {
|
||||
statusCode: 400,
|
||||
body: {
|
||||
statusCode: 400,
|
||||
error: 'Bad Request',
|
||||
message: '"tempToken" is invalid'
|
||||
}
|
||||
}
|
||||
}
|
28
cypress/fixtures/users/signin/post.ts
Normal file
28
cypress/fixtures/users/signin/post.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { Handler } from '../../handler'
|
||||
|
||||
export const postUsersSigninHandler: Handler = {
|
||||
method: 'POST',
|
||||
url: '/users/signin',
|
||||
response: {
|
||||
statusCode: 200,
|
||||
body: {
|
||||
accessToken: 'access-token',
|
||||
refreshToken: 'refresh-token',
|
||||
expiresIn: 3600000,
|
||||
type: 'Bearer'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const postUsersSigninInvalidCredentialsHandler: Handler = {
|
||||
method: 'POST',
|
||||
url: '/users/signin',
|
||||
response: {
|
||||
statusCode: 400,
|
||||
body: {
|
||||
statusCode: 400,
|
||||
error: 'Bad Request',
|
||||
message: 'Invalid credentials.'
|
||||
}
|
||||
}
|
||||
}
|
30
cypress/fixtures/users/signup/post.ts
Normal file
30
cypress/fixtures/users/signup/post.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import { Handler } from '../../handler'
|
||||
|
||||
import { user, userSettings } from '../user'
|
||||
|
||||
export const postUsersSignupHandler: Handler = {
|
||||
method: 'POST',
|
||||
url: '/users/signup',
|
||||
response: {
|
||||
statusCode: 201,
|
||||
body: {
|
||||
user: {
|
||||
...user,
|
||||
settings: userSettings
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const postUsersSignupAlreadyUsedHandler: Handler = {
|
||||
method: 'POST',
|
||||
url: '/users/signup',
|
||||
response: {
|
||||
statusCode: 400,
|
||||
body: {
|
||||
statusCode: 400,
|
||||
error: 'Bad Request',
|
||||
message: 'body.email or body.name already taken.'
|
||||
}
|
||||
}
|
||||
}
|
26
cypress/fixtures/users/user.ts
Normal file
26
cypress/fixtures/users/user.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import { UserSettings } from '../../../models/UserSettings'
|
||||
import { UserPublic } from '../../../models/User'
|
||||
|
||||
export const user: UserPublic = {
|
||||
id: 1,
|
||||
name: 'Divlo',
|
||||
email: 'contact@divlo.fr',
|
||||
logo: undefined,
|
||||
status: undefined,
|
||||
biography: undefined,
|
||||
website: 'https://divlo.fr',
|
||||
isConfirmed: true,
|
||||
createdAt: '2021-10-20T20:30:51.595Z',
|
||||
updatedAt: '2021-10-20T20:59:08.485Z'
|
||||
}
|
||||
|
||||
export const userSettings: UserSettings = {
|
||||
id: 1,
|
||||
language: 'en',
|
||||
theme: 'dark',
|
||||
isPublicEmail: false,
|
||||
isPublicGuilds: false,
|
||||
createdAt: '2021-10-20T20:30:51.605Z',
|
||||
updatedAt: '2021-10-22T07:22:07.956Z',
|
||||
userId: 1
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
import { authenticationHandlers } from '../../../../fixtures/handler'
|
||||
|
||||
describe('Pages > /application/[guildId]/[channelId]', () => {
|
||||
beforeEach(() => {
|
||||
cy.task('stopMockServer')
|
||||
})
|
||||
|
||||
it('should redirect the user to `/application` if `guildId` or `channelId` are not numbers', () => {
|
||||
cy.task('startMockServer', authenticationHandlers).setCookie(
|
||||
'refreshToken',
|
||||
'refresh-token'
|
||||
)
|
||||
cy.visit('/application/abc/abc')
|
||||
.location('pathname')
|
||||
.should('eq', '/application')
|
||||
})
|
||||
})
|
35
cypress/integration/pages/application/index.spec.ts
Normal file
35
cypress/integration/pages/application/index.spec.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import { authenticationHandlers } from '../../../fixtures/handler'
|
||||
|
||||
const applicationPaths = [
|
||||
'/application',
|
||||
'/application/users/0',
|
||||
'/application/guilds/create',
|
||||
'/application/guilds/join',
|
||||
'/application/0/0'
|
||||
]
|
||||
|
||||
describe('Pages > /application', () => {
|
||||
beforeEach(() => {
|
||||
cy.task('stopMockServer')
|
||||
})
|
||||
|
||||
it('should redirect the user to `/authentication/signin` if not signed in', () => {
|
||||
for (const applicationPath of applicationPaths) {
|
||||
cy.visit(applicationPath)
|
||||
.location('pathname')
|
||||
.should('eq', '/authentication/signin')
|
||||
}
|
||||
})
|
||||
|
||||
it('should not redirect the user if signed in', () => {
|
||||
cy.task('startMockServer', authenticationHandlers).setCookie(
|
||||
'refreshToken',
|
||||
'refresh-token'
|
||||
)
|
||||
for (const applicationPath of applicationPaths) {
|
||||
cy.visit(applicationPath)
|
||||
.location('pathname')
|
||||
.should('eq', applicationPath)
|
||||
}
|
||||
})
|
||||
})
|
@ -0,0 +1,37 @@
|
||||
import { postUsersResetPasswordHandler } from '../../../fixtures/users/reset-password/post'
|
||||
import { user } from '../../../fixtures/users/user'
|
||||
|
||||
describe('Pages > /authentication/forgot-password', () => {
|
||||
beforeEach(() => {
|
||||
cy.task('stopMockServer')
|
||||
cy.visit('/authentication/forgot-password')
|
||||
})
|
||||
|
||||
it('should succeeds and sends a password-reset request', () => {
|
||||
cy.task('startMockServer', [postUsersResetPasswordHandler])
|
||||
cy.get('#message').should('not.exist')
|
||||
cy.get('[data-cy=input-email]').type(user.email)
|
||||
cy.get('[data-cy=submit]').click()
|
||||
cy.get('#message').should(
|
||||
'have.text',
|
||||
'Success: Password-reset request successful, please check your emails!'
|
||||
)
|
||||
})
|
||||
|
||||
it('should fails with unreachable api server', () => {
|
||||
cy.get('#message').should('not.exist')
|
||||
cy.get('[data-cy=input-email]').type(user.email)
|
||||
cy.get('[data-cy=submit]').click()
|
||||
cy.get('#message').should('have.text', 'Error: Internal Server Error.')
|
||||
})
|
||||
|
||||
it('should fails with wrong email format', () => {
|
||||
cy.get('#message').should('not.exist')
|
||||
cy.get('[data-cy=input-email]').type('test')
|
||||
cy.get('[data-cy=submit]').click()
|
||||
cy.get('#message').should(
|
||||
'have.text',
|
||||
'Error: Mmm… It seems that this email is not valid 🤔.'
|
||||
)
|
||||
})
|
||||
})
|
@ -0,0 +1,45 @@
|
||||
import {
|
||||
putUsersResetPasswordHandler,
|
||||
putUsersResetPasswordInvalidTemporaryTokenHandler
|
||||
} from '../../../fixtures/users/reset-password/put'
|
||||
|
||||
describe('Pages > /authentication/reset-password', () => {
|
||||
beforeEach(() => {
|
||||
cy.task('stopMockServer')
|
||||
})
|
||||
|
||||
it('should succeeds and redirect user to sign in page', () => {
|
||||
cy.task('startMockServer', [putUsersResetPasswordHandler])
|
||||
cy.visit('/authentication/reset-password?temporaryToken=abcdefg')
|
||||
cy.get('#message').should('not.exist')
|
||||
cy.get('[data-cy=input-password]').type('somepassword')
|
||||
cy.get('[data-cy=submit]').click()
|
||||
cy.location('pathname').should('eq', '/authentication/signin')
|
||||
})
|
||||
|
||||
it('should fails with invalid `temporaryToken`', () => {
|
||||
cy.task('startMockServer', [
|
||||
putUsersResetPasswordInvalidTemporaryTokenHandler
|
||||
])
|
||||
cy.visit('/authentication/reset-password')
|
||||
cy.get('#message').should('not.exist')
|
||||
cy.get('[data-cy=input-password]').type('somepassword')
|
||||
cy.get('[data-cy=submit]').click()
|
||||
cy.get('#message').should('have.text', 'Error: Invalid value.')
|
||||
})
|
||||
|
||||
it('should fails with unreachable api server', () => {
|
||||
cy.visit('/authentication/reset-password')
|
||||
cy.get('#message').should('not.exist')
|
||||
cy.get('[data-cy=input-password]').type('randompassword')
|
||||
cy.get('[data-cy=submit]').click()
|
||||
cy.get('#message').should('have.text', 'Error: Internal Server Error.')
|
||||
})
|
||||
|
||||
it('should fails with empty password value', () => {
|
||||
cy.visit('/authentication/reset-password')
|
||||
cy.get('#message').should('not.exist')
|
||||
cy.get('[data-cy=submit]').click()
|
||||
cy.get('#message').should('have.text', 'Error: Invalid value.')
|
||||
})
|
||||
})
|
55
cypress/integration/pages/authentication/signin.spec.ts
Normal file
55
cypress/integration/pages/authentication/signin.spec.ts
Normal file
@ -0,0 +1,55 @@
|
||||
import { authenticationHandlers } from '../../../fixtures/handler'
|
||||
import {
|
||||
postUsersSigninHandler,
|
||||
postUsersSigninInvalidCredentialsHandler
|
||||
} from 'cypress/fixtures/users/signin/post'
|
||||
import { user } from '../../../fixtures/users/user'
|
||||
|
||||
describe('Pages > /authentication/signin', () => {
|
||||
beforeEach(() => {
|
||||
cy.task('stopMockServer')
|
||||
cy.visit('/authentication/signin')
|
||||
})
|
||||
|
||||
it('should succeeds and sign in the user', () => {
|
||||
cy.task('startMockServer', [
|
||||
...authenticationHandlers,
|
||||
postUsersSigninHandler
|
||||
])
|
||||
cy.get('#error-email').should('not.exist')
|
||||
cy.get('#error-password').should('not.exist')
|
||||
cy.get('[data-cy=input-email]').type(user.email)
|
||||
cy.get('[data-cy=input-password]').type('randompassword')
|
||||
cy.get('[data-cy=submit]').click()
|
||||
cy.location('pathname').should('eq', '/application')
|
||||
})
|
||||
|
||||
it('should fails with unreachable api server', () => {
|
||||
cy.get('#error-email').should('not.exist')
|
||||
cy.get('#error-password').should('not.exist')
|
||||
cy.get('[data-cy=input-email]').type(user.email)
|
||||
cy.get('[data-cy=input-password]').type('randompassword')
|
||||
cy.get('[data-cy=submit]').click()
|
||||
cy.get('#message').should('have.text', 'Error: Internal Server Error.')
|
||||
cy.get('#error-email').should('not.exist')
|
||||
cy.get('#error-password').should('not.exist')
|
||||
})
|
||||
|
||||
it('should fails with invalid credentials', () => {
|
||||
cy.task('startMockServer', [
|
||||
...authenticationHandlers,
|
||||
postUsersSigninInvalidCredentialsHandler
|
||||
])
|
||||
cy.get('#error-email').should('not.exist')
|
||||
cy.get('#error-password').should('not.exist')
|
||||
cy.get('[data-cy=input-email]').type(user.email)
|
||||
cy.get('[data-cy=input-password]').type('randompassword')
|
||||
cy.get('[data-cy=submit]').click()
|
||||
cy.get('#message').should(
|
||||
'have.text',
|
||||
'Error: Invalid credentials. Please try again.'
|
||||
)
|
||||
cy.get('#error-email').should('not.exist')
|
||||
cy.get('#error-password').should('not.exist')
|
||||
})
|
||||
})
|
85
cypress/integration/pages/authentication/signup.spec.ts
Normal file
85
cypress/integration/pages/authentication/signup.spec.ts
Normal file
@ -0,0 +1,85 @@
|
||||
import { user } from '../../../fixtures/users/user'
|
||||
import {
|
||||
postUsersSignupHandler,
|
||||
postUsersSignupAlreadyUsedHandler
|
||||
} from '../../../fixtures/users/signup/post'
|
||||
|
||||
describe('Pages > /authentication/signup', () => {
|
||||
beforeEach(() => {
|
||||
cy.task('stopMockServer')
|
||||
cy.visit('/authentication/signup')
|
||||
})
|
||||
|
||||
it('should succeeds and sign up the user', () => {
|
||||
cy.task('startMockServer', [postUsersSignupHandler])
|
||||
cy.get('#error-name').should('not.exist')
|
||||
cy.get('#error-email').should('not.exist')
|
||||
cy.get('#error-password').should('not.exist')
|
||||
cy.get('[data-cy=input-name]').type(user.name)
|
||||
cy.get('[data-cy=input-email]').type(user.email)
|
||||
cy.get('[data-cy=input-password]').type('randompassword')
|
||||
cy.get('[data-cy=submit]').click()
|
||||
cy.get('#message').should(
|
||||
'have.text',
|
||||
"Success: You're almost there, please check your emails to confirm registration."
|
||||
)
|
||||
})
|
||||
|
||||
it('should fails with name or email already used', () => {
|
||||
cy.task('startMockServer', [postUsersSignupAlreadyUsedHandler])
|
||||
cy.get('#error-name').should('not.exist')
|
||||
cy.get('#error-email').should('not.exist')
|
||||
cy.get('#error-password').should('not.exist')
|
||||
cy.get('[data-cy=input-name]').type(user.name)
|
||||
cy.get('[data-cy=input-email]').type(user.email)
|
||||
cy.get('[data-cy=input-password]').type('randompassword')
|
||||
cy.get('[data-cy=submit]').click()
|
||||
cy.get('#message').should('have.text', 'Error: Name or Email already used.')
|
||||
cy.get('#error-name').should('not.exist')
|
||||
cy.get('#error-email').should('not.exist')
|
||||
cy.get('#error-password').should('not.exist')
|
||||
})
|
||||
|
||||
it('should fails with unreachable api server', () => {
|
||||
cy.get('#error-name').should('not.exist')
|
||||
cy.get('#error-email').should('not.exist')
|
||||
cy.get('#error-password').should('not.exist')
|
||||
cy.get('[data-cy=input-name]').type(user.name)
|
||||
cy.get('[data-cy=input-email]').type(user.email)
|
||||
cy.get('[data-cy=input-password]').type('randompassword')
|
||||
cy.get('[data-cy=submit]').click()
|
||||
cy.get('#message').should('have.text', 'Error: Internal Server Error.')
|
||||
cy.get('#error-name').should('not.exist')
|
||||
cy.get('#error-email').should('not.exist')
|
||||
cy.get('#error-password').should('not.exist')
|
||||
})
|
||||
|
||||
it('should fails with all inputs as required with error messages and update error messages when updating language (translation)', () => {
|
||||
const requiredErrorMessage = {
|
||||
en: 'Error: Oops, this field is required 🙈.',
|
||||
fr: 'Erreur: Oups, ce champ est obligatoire 🙈.'
|
||||
}
|
||||
cy.get('#error-name').should('not.exist')
|
||||
cy.get('#error-email').should('not.exist')
|
||||
cy.get('#error-password').should('not.exist')
|
||||
cy.get('[data-cy=submit]').click()
|
||||
cy.get('#error-name').should('have.text', requiredErrorMessage.en)
|
||||
cy.get('#error-email').should('have.text', requiredErrorMessage.en)
|
||||
cy.get('#error-password').should('have.text', requiredErrorMessage.en)
|
||||
cy.get('[data-cy=language-click]').click()
|
||||
cy.get('[data-cy=languages-list] > li:first-child').contains('FR').click()
|
||||
cy.get('#error-name').should('have.text', requiredErrorMessage.fr)
|
||||
cy.get('#error-email').should('have.text', requiredErrorMessage.fr)
|
||||
cy.get('#error-password').should('have.text', requiredErrorMessage.fr)
|
||||
})
|
||||
|
||||
it('should fails with wrong email format', () => {
|
||||
cy.get('#error-email').should('not.exist')
|
||||
cy.get('[data-cy=input-email]').type('test')
|
||||
cy.get('[data-cy=submit]').click()
|
||||
cy.get('#error-email').should(
|
||||
'have.text',
|
||||
'Error: Mmm… It seems that this email is not valid 🤔.'
|
||||
)
|
||||
})
|
||||
})
|
12
cypress/integration/pages/index.spec.ts
Normal file
12
cypress/integration/pages/index.spec.ts
Normal file
@ -0,0 +1,12 @@
|
||||
describe('Page > /', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/')
|
||||
})
|
||||
|
||||
it('should redirect the user to signup page when clicking "Get started"', () => {
|
||||
cy.get('[data-cy=get-started]')
|
||||
.click()
|
||||
.location('pathname')
|
||||
.should('eq', '/authentication/signup')
|
||||
})
|
||||
})
|
40
cypress/plugins/index.js
Normal file
40
cypress/plugins/index.js
Normal file
@ -0,0 +1,40 @@
|
||||
import { getLocal } from 'mockttp'
|
||||
|
||||
/// <reference types="cypress" />
|
||||
|
||||
/** @type {import('mockttp').Mockttp | null} */
|
||||
let server = null
|
||||
|
||||
/**
|
||||
* @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(8080)
|
||||
for (const handler of handlers) {
|
||||
await server[handler.method.toLowerCase()](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
|
||||
}
|
Reference in New Issue
Block a user