mirror of
https://github.com/mattermost/focalboard.git
synced 2024-11-24 08:22:29 +02:00
[GH-42] Cypress tests for login actions (#1679)
* Testing API added to server: - registered only if `enableTestingAPI` is set to `true` in the config file - has only one route `test/reset` - reset clears the tables in db for blocks, users, sessions - functions `DeleteAllBlocks` and `DeleteAllUsers` added to `Store` interface - new functions implemented for `SQLStore` * Cypress tests (initial version) for login actions added: - redirect to login page, - register user, - test for loading home page deleted, - allow js in `tsconfig.json` for cypress tests. * Cypress tests for login actions: - check that main page with workspace is visible after registration, - initial version of test for login of register user. * Cypress tests for login actions: - function for checking that workspace is available added, - functions for login and logout added, - test for password change added, - session parameters added to server config for cypress testing. * Switch Cypress tests to typescript. * Use ids for inputs instead of placeholder text. * Use cypress request for login without loading login page. * Cypress custom command for login added. * Cypress tests fixed: - new cypress commands for server reset, register/login user - single test for "create and delete board/card" - fixes for `BoardPage` component useEffect callbacks - npm script `runserver-test` doesn't use single user mode * Deletion of all blocks changed: - also deletes blocks from history - public function renamed to DeleteAllBlocksPermanently - code for mocks and public methods generated * Server tests for files fixed on windows. * Cypress tests for the registration of second user via invite link added. * Added `baseUrl` in main `tsconfig.json` (required by cypress configuration). * Cypress test fixed. Comments as well as log messages added. * Log a message if testing API is enabled. * Single cypress test for register/login actions. * Revert changes to server. * More convenient cypress commands: - all API calls made as separate commands - declarations for commands moved to separate global.d.ts file - utility functions moved after test actions in 'Login actions' test
This commit is contained in:
parent
e0657dad9a
commit
a498149a76
@ -1,6 +1,15 @@
|
|||||||
{
|
{
|
||||||
"chromeWebSecurity": false,
|
"chromeWebSecurity": false,
|
||||||
"baseUrl": "http://localhost:8088",
|
"baseUrl": "http://localhost:8088",
|
||||||
|
"testFiles": [
|
||||||
|
"**/login*.ts",
|
||||||
|
"**/create*.ts"
|
||||||
|
],
|
||||||
|
"env": {
|
||||||
|
"username": "test-user",
|
||||||
|
"password": "test-password",
|
||||||
|
"email": "test@mail.com"
|
||||||
|
},
|
||||||
"video": false,
|
"video": false,
|
||||||
"viewportWidth": 1600,
|
"viewportWidth": 1600,
|
||||||
"viewportHeight": 1200
|
"viewportHeight": 1200
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
{
|
{
|
||||||
"serverRoot": "http://localhost:8088",
|
"serverRoot": "http://localhost:8088",
|
||||||
"port": 8088,
|
"port": 8088,
|
||||||
"dbtype": "sqlite3",
|
"dbtype": "sqlite3",
|
||||||
"dbconfig": "file::memory:?cache=shared",
|
"dbconfig": "file::memory:?cache=shared",
|
||||||
"useSSL": false,
|
"useSSL": false,
|
||||||
"webpath": "../pack",
|
"webpath": "../pack",
|
||||||
"filespath": "../../files",
|
"filespath": "../../files",
|
||||||
"telemetry": false,
|
"telemetry": false,
|
||||||
"webhook_update": []
|
"webhook_update": [],
|
||||||
|
"session_expire_time": 2592000,
|
||||||
|
"session_refresh_time": 18000
|
||||||
}
|
}
|
||||||
|
19
webapp/cypress/global.d.ts
vendored
Normal file
19
webapp/cypress/global.d.ts
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
|
declare namespace Cypress {
|
||||||
|
type LoginData = {
|
||||||
|
username: string
|
||||||
|
password: string
|
||||||
|
}
|
||||||
|
type UserData = LoginData & {
|
||||||
|
email: string
|
||||||
|
}
|
||||||
|
interface Chainable {
|
||||||
|
apiRegisterUser: (data: UserData, token?: string, failOnError?: boolean) => Chainable
|
||||||
|
apiLoginUser: (data: LoginData) => Chainable
|
||||||
|
apiGetMe: () => Chainable<string>
|
||||||
|
apiChangePassword: (userId: string, oldPassword: string, newPassword: string) => Chainable
|
||||||
|
apiInitServer: () => Chainable
|
||||||
|
}
|
||||||
|
}
|
@ -1,134 +0,0 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
||||||
// See LICENSE.txt for license information.
|
|
||||||
|
|
||||||
/* eslint-disable max-nested-callbacks */
|
|
||||||
|
|
||||||
/// <reference types="Cypress" />
|
|
||||||
|
|
||||||
describe('Create and delete board / card', () => {
|
|
||||||
const timestamp = new Date().toLocaleString();
|
|
||||||
const boardTitle = `Test Board (${timestamp})`;
|
|
||||||
const cardTitle = `Test Card (${timestamp})`;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
localStorage.setItem('focalboardSessionId', 'TESTTOKEN');
|
|
||||||
localStorage.setItem('language', 'en');
|
|
||||||
cy.expect(localStorage.getItem('focalboardSessionId')).to.eq('TESTTOKEN');
|
|
||||||
localStorage.setItem('welcomePageViewed', 'true');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Can create and delete a board and card', () => {
|
|
||||||
cy.visit('/');
|
|
||||||
cy.contains('+ Add board').click({force: true});
|
|
||||||
cy.contains('Empty board').click({force: true});
|
|
||||||
cy.get('.BoardComponent').should('exist');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Can set the board title', () => {
|
|
||||||
// Board title
|
|
||||||
cy.get('.Editable.title').
|
|
||||||
type(boardTitle).
|
|
||||||
type('{enter}').
|
|
||||||
should('have.value', boardTitle);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Can hide and show the sidebar with active board', async () => {
|
|
||||||
// Hide and show the sidebar option available on mobile devices
|
|
||||||
cy.viewport(767, 1024);
|
|
||||||
cy.get('.sidebarSwitcher').click();
|
|
||||||
cy.get('.Sidebar .heading').should('not.exist');
|
|
||||||
cy.get('.Sidebar .show-button').click();
|
|
||||||
cy.get('.Sidebar .heading').should('exist');
|
|
||||||
|
|
||||||
cy.viewport(1024, 1024);
|
|
||||||
cy.get('.sidebarSwitcher').should('not.exist');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Can rename the board view', () => {
|
|
||||||
// Rename board view
|
|
||||||
const boardViewTitle = `Test board (${timestamp})`;
|
|
||||||
cy.get('.ViewHeader>.Editable[title=\'Board view\']').should('exist');
|
|
||||||
cy.get('.ViewHeader>.Editable').
|
|
||||||
clear().
|
|
||||||
type(boardViewTitle).
|
|
||||||
type('{esc}');
|
|
||||||
|
|
||||||
cy.get(`.ViewHeader .Editable[title='${boardViewTitle}']`).should('exist');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Can create a card', () => {
|
|
||||||
// Create card
|
|
||||||
cy.get('.ViewHeader').contains('New').click();
|
|
||||||
cy.get('.CardDetail').should('exist');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Can set the card title', () => {
|
|
||||||
// Card title
|
|
||||||
cy.get('.CardDetail .EditableArea.title').
|
|
||||||
type(cardTitle).
|
|
||||||
type('{enter}').
|
|
||||||
should('have.value', cardTitle);
|
|
||||||
|
|
||||||
// Close card
|
|
||||||
cy.get('.Dialog.dialog-back .wrapper').click({force: true});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Can create a card by clicking on the + button', () => {
|
|
||||||
// Create a card by clicking on the + button
|
|
||||||
cy.get('.KanbanColumnHeader :nth-child(5) > .CompassIcon ').click();
|
|
||||||
cy.get('.CardDetail').should('exist');
|
|
||||||
|
|
||||||
// Close card
|
|
||||||
cy.get('.Dialog.dialog-back .wrapper').click({force: true});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Can create a table view', () => {
|
|
||||||
// Create table view
|
|
||||||
// cy.intercept('POST', '/api/v1/blocks').as('insertBlocks');
|
|
||||||
cy.get('.ViewHeader').get('.DropdownIcon').first().parent().click();
|
|
||||||
cy.get('.ViewHeader').contains('Add view').click();
|
|
||||||
cy.get('.ViewHeader').contains('Add view').click();
|
|
||||||
cy.get('.ViewHeader').contains('Add view').parent().contains('Table').click();
|
|
||||||
|
|
||||||
// cy.wait('@insertBlocks');
|
|
||||||
|
|
||||||
// Wait for round-trip to complete and DOM to update
|
|
||||||
cy.get('.ViewHeader .Editable[title=\'Table view\']').should('exist');
|
|
||||||
|
|
||||||
// Card should exist in table
|
|
||||||
cy.get(`.TableRow [value='${cardTitle}']`).should('exist');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Can rename the table view', () => {
|
|
||||||
// Rename table view
|
|
||||||
const tableViewTitle = `Test table (${timestamp})`;
|
|
||||||
cy.get('.ViewHeader .Editable[title=\'Table view\']').
|
|
||||||
clear().
|
|
||||||
type(tableViewTitle).
|
|
||||||
type('{esc}');
|
|
||||||
|
|
||||||
cy.get(`.ViewHeader .Editable[title='${tableViewTitle}']`).should('exist');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Can sort the table', () => {
|
|
||||||
// Sort
|
|
||||||
cy.get('.ViewHeader').contains('Sort').click();
|
|
||||||
cy.get('.ViewHeader').contains('Sort').parent().contains('Name').click();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Can delete the board', () => {
|
|
||||||
// Delete board
|
|
||||||
cy.get('.Sidebar .octo-sidebar-list').
|
|
||||||
contains(boardTitle).first().
|
|
||||||
next().
|
|
||||||
find('.Button.IconButton').
|
|
||||||
click({force: true});
|
|
||||||
|
|
||||||
cy.contains('Delete board').click({force: true});
|
|
||||||
|
|
||||||
cy.get('.DeleteBoardDialog button.danger').click({force: true});
|
|
||||||
|
|
||||||
// Board should not exist
|
|
||||||
cy.contains(boardTitle).should('not.exist');
|
|
||||||
});
|
|
||||||
});
|
|
112
webapp/cypress/integration/createBoard.ts
Normal file
112
webapp/cypress/integration/createBoard.ts
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
|
describe('Create and delete board / card', () => {
|
||||||
|
const timestamp = new Date().toLocaleString()
|
||||||
|
const boardTitle = `Test Board (${timestamp})`
|
||||||
|
const cardTitle = `Test Card (${timestamp})`
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.apiInitServer()
|
||||||
|
localStorage.setItem('welcomePageViewed', 'true')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Can create and delete a board and a card', () => {
|
||||||
|
cy.visit('/')
|
||||||
|
|
||||||
|
// Create new empty board
|
||||||
|
cy.log('**Create new empty board**')
|
||||||
|
cy.contains('+ Add board').click({force: true})
|
||||||
|
cy.contains('Empty board').click({force: true})
|
||||||
|
cy.get('.BoardComponent').should('exist')
|
||||||
|
|
||||||
|
// Change board title
|
||||||
|
cy.log('**Change board title**')
|
||||||
|
cy.get('.Editable.title').
|
||||||
|
type(boardTitle).
|
||||||
|
type('{enter}').
|
||||||
|
should('have.value', boardTitle)
|
||||||
|
|
||||||
|
// Hide and show the sidebar
|
||||||
|
cy.log('**Hide and show the sidebar**')
|
||||||
|
cy.get('.sidebarSwitcher').click()
|
||||||
|
cy.get('.Sidebar .heading').should('not.exist')
|
||||||
|
cy.get('.Sidebar .show-button').click()
|
||||||
|
cy.get('.Sidebar .heading').should('exist')
|
||||||
|
|
||||||
|
// Rename board view
|
||||||
|
cy.log('**Rename board view**')
|
||||||
|
const boardViewTitle = `Test board (${timestamp})`
|
||||||
|
cy.get(".ViewHeader>.Editable[title='Board view']").should('exist')
|
||||||
|
cy.get('.ViewHeader>.Editable').
|
||||||
|
clear().
|
||||||
|
type(boardViewTitle).
|
||||||
|
type('{esc}')
|
||||||
|
cy.get(`.ViewHeader .Editable[title='${boardViewTitle}']`).should('exist')
|
||||||
|
|
||||||
|
// Create card
|
||||||
|
cy.log('**Create card**')
|
||||||
|
cy.get('.ViewHeader').contains('New').click()
|
||||||
|
cy.get('.CardDetail').should('exist')
|
||||||
|
|
||||||
|
// Change card title
|
||||||
|
cy.log('**Change card title**')
|
||||||
|
cy.get('.CardDetail .EditableArea.title').
|
||||||
|
type(cardTitle).
|
||||||
|
type('{enter}').
|
||||||
|
should('have.value', cardTitle)
|
||||||
|
|
||||||
|
// Close card dialog
|
||||||
|
cy.log('**Close card dialog**')
|
||||||
|
cy.get('.Dialog.dialog-back .wrapper').click({force: true})
|
||||||
|
|
||||||
|
// Create a card by clicking on the + button
|
||||||
|
cy.log('**Create a card by clicking on the + button**')
|
||||||
|
cy.get('.KanbanColumnHeader .Button .AddIcon').click()
|
||||||
|
cy.get('.CardDetail').should('exist')
|
||||||
|
cy.get('.Dialog.dialog-back .wrapper').click({force: true})
|
||||||
|
|
||||||
|
// Create table view
|
||||||
|
cy.log('**Create table view**')
|
||||||
|
cy.get('.ViewHeader').get('.DropdownIcon').first().parent().click()
|
||||||
|
cy.get('.ViewHeader').contains('Add view').click()
|
||||||
|
cy.get('.ViewHeader').contains('Add view').click()
|
||||||
|
cy.get('.ViewHeader').
|
||||||
|
contains('Add view').
|
||||||
|
parent().
|
||||||
|
contains('Table').
|
||||||
|
click()
|
||||||
|
cy.get(".ViewHeader .Editable[title='Table view']").should('exist')
|
||||||
|
cy.get(`.TableRow [value='${cardTitle}']`).should('exist')
|
||||||
|
|
||||||
|
// Rename table view
|
||||||
|
cy.log('**Rename table view**')
|
||||||
|
const tableViewTitle = `Test table (${timestamp})`
|
||||||
|
cy.get(".ViewHeader .Editable[title='Table view']").
|
||||||
|
clear().
|
||||||
|
type(tableViewTitle).
|
||||||
|
type('{esc}')
|
||||||
|
cy.get(`.ViewHeader .Editable[title='${tableViewTitle}']`).should('exist')
|
||||||
|
|
||||||
|
// Sort the table
|
||||||
|
cy.log('**Sort the table**')
|
||||||
|
cy.get('.ViewHeader').contains('Sort').click()
|
||||||
|
cy.get('.ViewHeader').
|
||||||
|
contains('Sort').
|
||||||
|
parent().
|
||||||
|
contains('Name').
|
||||||
|
click()
|
||||||
|
|
||||||
|
// Delete board
|
||||||
|
cy.log('**Delete board**')
|
||||||
|
cy.get('.Sidebar .octo-sidebar-list').
|
||||||
|
contains(boardTitle).
|
||||||
|
first().
|
||||||
|
next().
|
||||||
|
find('.Button.IconButton').
|
||||||
|
click({force: true})
|
||||||
|
cy.contains('Delete board').click({force: true})
|
||||||
|
cy.get('.DeleteBoardDialog button.danger').click({force: true})
|
||||||
|
cy.contains(boardTitle).should('not.exist')
|
||||||
|
})
|
||||||
|
})
|
@ -1,9 +0,0 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
||||||
// See LICENSE.txt for license information.
|
|
||||||
|
|
||||||
describe('Load homepage', () => {
|
|
||||||
it('Can load homepage', () => {
|
|
||||||
cy.visit('/');
|
|
||||||
cy.get('div#focalboard-app').should('exist');
|
|
||||||
});
|
|
||||||
});
|
|
118
webapp/cypress/integration/loginActions.ts
Normal file
118
webapp/cypress/integration/loginActions.ts
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
|
describe('Login actions', () => {
|
||||||
|
const username = Cypress.env('username')
|
||||||
|
const email = Cypress.env('email')
|
||||||
|
const password = Cypress.env('password')
|
||||||
|
|
||||||
|
it('Can perform login/register actions', () => {
|
||||||
|
// Redirects to login page
|
||||||
|
cy.log('**Redirects to login page**')
|
||||||
|
cy.visit('/')
|
||||||
|
cy.location('pathname').should('eq', '/login')
|
||||||
|
cy.get('.LoginPage').contains('Log in')
|
||||||
|
cy.get('#login-username').should('exist')
|
||||||
|
cy.get('#login-password').should('exist')
|
||||||
|
cy.get('button').contains('Log in')
|
||||||
|
cy.get('a').contains('create an account')
|
||||||
|
|
||||||
|
// Can register a user
|
||||||
|
cy.log('**Can register a user**')
|
||||||
|
cy.visit('/login')
|
||||||
|
cy.get('a').contains('create an account').click()
|
||||||
|
cy.location('pathname').should('eq', '/register')
|
||||||
|
cy.get('.RegisterPage').contains('Sign up')
|
||||||
|
cy.get('#login-email').type(email)
|
||||||
|
cy.get('#login-username').type(username)
|
||||||
|
cy.get('#login-password').type(password)
|
||||||
|
cy.get('button').contains('Register').click()
|
||||||
|
workspaceIsAvailable()
|
||||||
|
|
||||||
|
// Can log out user
|
||||||
|
cy.log('**Can log out user**')
|
||||||
|
cy.get('.SidebarUserMenu').click()
|
||||||
|
cy.get('.menu-name').contains('Log out').click()
|
||||||
|
cy.location('pathname').should('eq', '/login')
|
||||||
|
|
||||||
|
// User should not be logged in automatically
|
||||||
|
cy.log('**User should not be logged in automatically**')
|
||||||
|
cy.visit('/')
|
||||||
|
cy.location('pathname').should('eq', '/login')
|
||||||
|
|
||||||
|
// Can log in registered user
|
||||||
|
cy.log('**Can log in registered user**')
|
||||||
|
loginUser(password)
|
||||||
|
|
||||||
|
// Can change password
|
||||||
|
cy.log('**Can change password**')
|
||||||
|
const newPassword = 'new_password'
|
||||||
|
cy.get('.SidebarUserMenu').click()
|
||||||
|
cy.get('.menu-name').contains('Change password').click()
|
||||||
|
cy.location('pathname').should('eq', '/change_password')
|
||||||
|
cy.get('.ChangePasswordPage').contains('Change Password')
|
||||||
|
cy.get('#login-oldpassword').type(password)
|
||||||
|
cy.get('#login-newpassword').type(newPassword)
|
||||||
|
cy.get('button').contains('Change password').click()
|
||||||
|
cy.get('.succeeded').click()
|
||||||
|
workspaceIsAvailable()
|
||||||
|
|
||||||
|
// Can log in user with new password
|
||||||
|
cy.log('**Can log in user with new password**')
|
||||||
|
loginUser(newPassword).then(() => resetPassword(newPassword))
|
||||||
|
|
||||||
|
// Can't register second user without invite link
|
||||||
|
cy.log('**Can\'t register second user without invite link**')
|
||||||
|
cy.visit('/register')
|
||||||
|
cy.get('#login-email').type(email)
|
||||||
|
cy.get('#login-username').type(username)
|
||||||
|
cy.get('#login-password').type(password)
|
||||||
|
cy.get('button').contains('Register').click()
|
||||||
|
cy.get('.error').contains('Invalid registration link').should('exist')
|
||||||
|
|
||||||
|
// Can register second user using invite link
|
||||||
|
cy.log('**Can register second user using invite link**')
|
||||||
|
|
||||||
|
// Copy invite link
|
||||||
|
cy.log('**Copy invite link**')
|
||||||
|
loginUser(password)
|
||||||
|
cy.get('.Sidebar .SidebarUserMenu').click()
|
||||||
|
cy.get('.menu-name').contains('Invite users').click()
|
||||||
|
cy.get('.Button').contains('Copy link').click()
|
||||||
|
cy.get('.Button').contains('Copied').should('exist')
|
||||||
|
|
||||||
|
cy.get('a.shareUrl').invoke('attr', 'href').then((inviteLink) => {
|
||||||
|
// Log out existing user
|
||||||
|
cy.log('**Log out existing user**')
|
||||||
|
cy.get('.Sidebar .SidebarUserMenu').click()
|
||||||
|
cy.get('.menu-name').contains('Log out').click()
|
||||||
|
|
||||||
|
// Register a new user
|
||||||
|
cy.log('**Register new user**')
|
||||||
|
cy.visit(inviteLink as string)
|
||||||
|
cy.get('#login-email').type('new-user@mail.com')
|
||||||
|
cy.get('#login-username').type('new-user')
|
||||||
|
cy.get('#login-password').type('new-password')
|
||||||
|
cy.get('button').contains('Register').click()
|
||||||
|
workspaceIsAvailable()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const workspaceIsAvailable = () => {
|
||||||
|
cy.location('pathname').should('eq', '/')
|
||||||
|
cy.get('.Workspace').should('exist')
|
||||||
|
return cy.get('.Sidebar').should('exist')
|
||||||
|
}
|
||||||
|
|
||||||
|
const loginUser = (withPassword: string) => {
|
||||||
|
cy.visit('/login')
|
||||||
|
cy.get('#login-username').type(username)
|
||||||
|
cy.get('#login-password').type(withPassword)
|
||||||
|
cy.get('button').contains('Log in').click()
|
||||||
|
return workspaceIsAvailable()
|
||||||
|
}
|
||||||
|
|
||||||
|
const resetPassword = (oldPassword: string) => {
|
||||||
|
cy.apiGetMe().then((userId) => cy.apiChangePassword(userId, oldPassword, password))
|
||||||
|
}
|
||||||
|
})
|
@ -1,25 +0,0 @@
|
|||||||
// ***********************************************
|
|
||||||
// This example commands.js shows you how to
|
|
||||||
// create various custom commands and overwrite
|
|
||||||
// existing commands.
|
|
||||||
//
|
|
||||||
// For more comprehensive examples of custom
|
|
||||||
// commands please read more here:
|
|
||||||
// https://on.cypress.io/custom-commands
|
|
||||||
// ***********************************************
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// -- This is a parent command --
|
|
||||||
// Cypress.Commands.add("login", (email, password) => { ... })
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// -- This is a child command --
|
|
||||||
// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// -- This is a dual command --
|
|
||||||
// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// -- This will overwrite an existing command --
|
|
||||||
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
|
|
68
webapp/cypress/support/commands.ts
Normal file
68
webapp/cypress/support/commands.ts
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
|
Cypress.Commands.add('apiRegisterUser', (data: Cypress.UserData, token?: string, failOnError?: boolean) => {
|
||||||
|
return cy.request({
|
||||||
|
method: 'POST',
|
||||||
|
url: '/api/v1/register',
|
||||||
|
body: {
|
||||||
|
...data,
|
||||||
|
token,
|
||||||
|
},
|
||||||
|
headers: {
|
||||||
|
'X-Requested-With': 'XMLHttpRequest',
|
||||||
|
},
|
||||||
|
failOnStatusCode: failOnError,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
Cypress.Commands.add('apiLoginUser', (data: Cypress.LoginData) => {
|
||||||
|
return cy.request({
|
||||||
|
method: 'POST',
|
||||||
|
url: '/api/v1/login',
|
||||||
|
body: {
|
||||||
|
...data,
|
||||||
|
type: 'normal',
|
||||||
|
},
|
||||||
|
headers: {
|
||||||
|
'X-Requested-With': 'XMLHttpRequest',
|
||||||
|
},
|
||||||
|
}).then((response) => {
|
||||||
|
expect(response.body).to.have.property('token')
|
||||||
|
localStorage.setItem('focalboardSessionId', response.body.token)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
Cypress.Commands.add('apiInitServer', () => {
|
||||||
|
const data: Cypress.UserData = {
|
||||||
|
username: Cypress.env('username'),
|
||||||
|
password: Cypress.env('password'),
|
||||||
|
email: Cypress.env('email'),
|
||||||
|
}
|
||||||
|
return cy.apiRegisterUser(data, '', false).apiLoginUser(data)
|
||||||
|
})
|
||||||
|
|
||||||
|
const headers = () => ({
|
||||||
|
headers: {
|
||||||
|
'X-Requested-With': 'XMLHttpRequest',
|
||||||
|
Authorization: `Bearer ${localStorage.getItem('focalboardSessionId')}`,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
Cypress.Commands.add('apiGetMe', () => {
|
||||||
|
return cy.request({
|
||||||
|
method: 'GET',
|
||||||
|
url: '/api/v1/users/me',
|
||||||
|
...headers(),
|
||||||
|
}).then((response) => response.body.id)
|
||||||
|
})
|
||||||
|
|
||||||
|
Cypress.Commands.add('apiChangePassword', (userId: string, oldPassword: string, newPassword: string) => {
|
||||||
|
const body = {oldPassword, newPassword}
|
||||||
|
return cy.request({
|
||||||
|
method: 'POST',
|
||||||
|
url: `/api/v1/users/${encodeURIComponent(userId)}/changepassword`,
|
||||||
|
...headers(),
|
||||||
|
body,
|
||||||
|
})
|
||||||
|
})
|
@ -1,20 +0,0 @@
|
|||||||
// ***********************************************************
|
|
||||||
// This example support/index.js is processed and
|
|
||||||
// loaded automatically before your test files.
|
|
||||||
//
|
|
||||||
// This is a great place to put global configuration and
|
|
||||||
// behavior that modifies Cypress.
|
|
||||||
//
|
|
||||||
// You can change the location of this file or turn off
|
|
||||||
// automatically serving support files with the
|
|
||||||
// 'supportFile' configuration option.
|
|
||||||
//
|
|
||||||
// You can read more here:
|
|
||||||
// https://on.cypress.io/configuration
|
|
||||||
// ***********************************************************
|
|
||||||
|
|
||||||
// Import commands.js using ES2015 syntax:
|
|
||||||
import './commands'
|
|
||||||
|
|
||||||
// Alternatively you can use CommonJS syntax:
|
|
||||||
// require('./commands')
|
|
4
webapp/cypress/support/index.ts
Normal file
4
webapp/cypress/support/index.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
|
import './commands'
|
@ -2,9 +2,11 @@
|
|||||||
"extends": "../tsconfig.json",
|
"extends": "../tsconfig.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
"types": ["cypress"]
|
"types": [
|
||||||
|
"cypress"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"**/*.ts"
|
"**/*.ts"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
"fix": "eslint --ext .tsx,.ts . --quiet --fix --cache && stylelint --fix **/*.scss",
|
"fix": "eslint --ext .tsx,.ts . --quiet --fix --cache && stylelint --fix **/*.scss",
|
||||||
"fix:scss": "prettier --write './src/**/*.scss'",
|
"fix:scss": "prettier --write './src/**/*.scss'",
|
||||||
"i18n-extract": "formatjs extract ../mattermost-plugin/webapp/src/*/*/*.ts? src/*/*/*.ts? src/*/*.ts? src/*.ts? --out-file i18n/tmp.json; formatjs compile i18n/tmp.json --out-file i18n/en.json; rm i18n/tmp.json",
|
"i18n-extract": "formatjs extract ../mattermost-plugin/webapp/src/*/*/*.ts? src/*/*/*.ts? src/*/*.ts? src/*.ts? --out-file i18n/tmp.json; formatjs compile i18n/tmp.json --out-file i18n/en.json; rm i18n/tmp.json",
|
||||||
"runserver-test": "cd cypress && cross-env FOCALBOARD_SINGLE_USER_TOKEN=TESTTOKEN ../../bin/focalboard-server -single-user",
|
"runserver-test": "cd cypress && \"../../bin/focalboard-server\"",
|
||||||
"cypress:ci": "start-server-and-test runserver-test http://localhost:8088 cypress:run",
|
"cypress:ci": "start-server-and-test runserver-test http://localhost:8088 cypress:run",
|
||||||
"cypress:run": "cypress run",
|
"cypress:run": "cypress run",
|
||||||
"cypress:run:chrome": "cypress run --browser chrome",
|
"cypress:run:chrome": "cypress run --browser chrome",
|
||||||
|
@ -187,9 +187,11 @@ const BoardPage = (props: Props): JSX.Element => {
|
|||||||
|
|
||||||
dispatch(loadAction(match.params.boardId))
|
dispatch(loadAction(match.params.boardId))
|
||||||
|
|
||||||
|
let subscribedToWorkspace = false
|
||||||
if (wsClient.state === 'open') {
|
if (wsClient.state === 'open') {
|
||||||
wsClient.authenticate(match.params.workspaceId || '0', token)
|
wsClient.authenticate(match.params.workspaceId || '0', token)
|
||||||
wsClient.subscribeToWorkspace(match.params.workspaceId || '0')
|
wsClient.subscribeToWorkspace(match.params.workspaceId || '0')
|
||||||
|
subscribedToWorkspace = true
|
||||||
}
|
}
|
||||||
|
|
||||||
const incrementalUpdate = (_: WSClient, blocks: Block[]) => {
|
const incrementalUpdate = (_: WSClient, blocks: Block[]) => {
|
||||||
@ -211,6 +213,7 @@ const BoardPage = (props: Props): JSX.Element => {
|
|||||||
const newToken = localStorage.getItem('focalboardSessionId') || ''
|
const newToken = localStorage.getItem('focalboardSessionId') || ''
|
||||||
wsClient.authenticate(match.params.workspaceId || '0', newToken)
|
wsClient.authenticate(match.params.workspaceId || '0', newToken)
|
||||||
wsClient.subscribeToWorkspace(match.params.workspaceId || '0')
|
wsClient.subscribeToWorkspace(match.params.workspaceId || '0')
|
||||||
|
subscribedToWorkspace = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (timeout) {
|
if (timeout) {
|
||||||
@ -220,6 +223,7 @@ const BoardPage = (props: Props): JSX.Element => {
|
|||||||
if (newState === 'close') {
|
if (newState === 'close') {
|
||||||
timeout = setTimeout(() => {
|
timeout = setTimeout(() => {
|
||||||
setWebsocketClosed(true)
|
setWebsocketClosed(true)
|
||||||
|
subscribedToWorkspace = false
|
||||||
}, websocketTimeoutForBanner)
|
}, websocketTimeoutForBanner)
|
||||||
} else {
|
} else {
|
||||||
setWebsocketClosed(false)
|
setWebsocketClosed(false)
|
||||||
@ -233,12 +237,14 @@ const BoardPage = (props: Props): JSX.Element => {
|
|||||||
if (timeout) {
|
if (timeout) {
|
||||||
clearTimeout(timeout)
|
clearTimeout(timeout)
|
||||||
}
|
}
|
||||||
wsClient.unsubscribeToWorkspace(match.params.workspaceId || '0')
|
if (subscribedToWorkspace) {
|
||||||
|
wsClient.unsubscribeToWorkspace(match.params.workspaceId || '0')
|
||||||
|
}
|
||||||
wsClient.removeOnChange(incrementalUpdate)
|
wsClient.removeOnChange(incrementalUpdate)
|
||||||
wsClient.removeOnReconnect(() => dispatch(loadAction(match.params.boardId)))
|
wsClient.removeOnReconnect(() => dispatch(loadAction(match.params.boardId)))
|
||||||
wsClient.removeOnStateChange(updateWebsocketState)
|
wsClient.removeOnStateChange(updateWebsocketState)
|
||||||
}
|
}
|
||||||
}, [match.params.workspaceId, props.readonly])
|
}, [match.params.workspaceId, props.readonly, match.params.boardId])
|
||||||
|
|
||||||
useHotkeys('ctrl+z,cmd+z', () => {
|
useHotkeys('ctrl+z,cmd+z', () => {
|
||||||
Utils.log('Undo')
|
Utils.log('Undo')
|
||||||
|
@ -1,27 +1,28 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"jsx": "react",
|
"jsx": "react",
|
||||||
"target": "es2019",
|
"target": "es2019",
|
||||||
"module": "commonjs",
|
"module": "commonjs",
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"noImplicitAny": true,
|
"noImplicitAny": true,
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"strictNullChecks": true,
|
"strictNullChecks": true,
|
||||||
"forceConsistentCasingInFileNames": true,
|
"forceConsistentCasingInFileNames": true,
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"incremental": false,
|
"incremental": false,
|
||||||
"outDir": "./dist",
|
"baseUrl": "src",
|
||||||
"moduleResolution": "node"
|
"outDir": "./dist",
|
||||||
},
|
"moduleResolution": "node"
|
||||||
"include": [
|
},
|
||||||
"."
|
"include": [
|
||||||
],
|
"."
|
||||||
"exclude": [
|
],
|
||||||
|
"exclude": [
|
||||||
".git",
|
".git",
|
||||||
"**/node_modules/*",
|
"**/node_modules/*",
|
||||||
"dist",
|
"dist",
|
||||||
"pack"
|
"pack"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user