mirror of
https://github.com/mattermost/focalboard.git
synced 2024-11-27 08:31:20 +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,
|
||||
"baseUrl": "http://localhost:8088",
|
||||
"testFiles": [
|
||||
"**/login*.ts",
|
||||
"**/create*.ts"
|
||||
],
|
||||
"env": {
|
||||
"username": "test-user",
|
||||
"password": "test-password",
|
||||
"email": "test@mail.com"
|
||||
},
|
||||
"video": false,
|
||||
"viewportWidth": 1600,
|
||||
"viewportHeight": 1200
|
||||
|
@ -1,11 +1,13 @@
|
||||
{
|
||||
"serverRoot": "http://localhost:8088",
|
||||
"port": 8088,
|
||||
"dbtype": "sqlite3",
|
||||
"dbconfig": "file::memory:?cache=shared",
|
||||
"useSSL": false,
|
||||
"webpath": "../pack",
|
||||
"filespath": "../../files",
|
||||
"telemetry": false,
|
||||
"webhook_update": []
|
||||
"serverRoot": "http://localhost:8088",
|
||||
"port": 8088,
|
||||
"dbtype": "sqlite3",
|
||||
"dbconfig": "file::memory:?cache=shared",
|
||||
"useSSL": false,
|
||||
"webpath": "../pack",
|
||||
"filespath": "../../files",
|
||||
"telemetry": false,
|
||||
"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",
|
||||
"compilerOptions": {
|
||||
"noEmit": true,
|
||||
"types": ["cypress"]
|
||||
"types": [
|
||||
"cypress"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"**/*.ts"
|
||||
]
|
||||
]
|
||||
}
|
||||
|
@ -13,7 +13,7 @@
|
||||
"fix": "eslint --ext .tsx,.ts . --quiet --fix --cache && stylelint --fix **/*.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",
|
||||
"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:run": "cypress run",
|
||||
"cypress:run:chrome": "cypress run --browser chrome",
|
||||
|
@ -187,9 +187,11 @@ const BoardPage = (props: Props): JSX.Element => {
|
||||
|
||||
dispatch(loadAction(match.params.boardId))
|
||||
|
||||
let subscribedToWorkspace = false
|
||||
if (wsClient.state === 'open') {
|
||||
wsClient.authenticate(match.params.workspaceId || '0', token)
|
||||
wsClient.subscribeToWorkspace(match.params.workspaceId || '0')
|
||||
subscribedToWorkspace = true
|
||||
}
|
||||
|
||||
const incrementalUpdate = (_: WSClient, blocks: Block[]) => {
|
||||
@ -211,6 +213,7 @@ const BoardPage = (props: Props): JSX.Element => {
|
||||
const newToken = localStorage.getItem('focalboardSessionId') || ''
|
||||
wsClient.authenticate(match.params.workspaceId || '0', newToken)
|
||||
wsClient.subscribeToWorkspace(match.params.workspaceId || '0')
|
||||
subscribedToWorkspace = true
|
||||
}
|
||||
|
||||
if (timeout) {
|
||||
@ -220,6 +223,7 @@ const BoardPage = (props: Props): JSX.Element => {
|
||||
if (newState === 'close') {
|
||||
timeout = setTimeout(() => {
|
||||
setWebsocketClosed(true)
|
||||
subscribedToWorkspace = false
|
||||
}, websocketTimeoutForBanner)
|
||||
} else {
|
||||
setWebsocketClosed(false)
|
||||
@ -233,12 +237,14 @@ const BoardPage = (props: Props): JSX.Element => {
|
||||
if (timeout) {
|
||||
clearTimeout(timeout)
|
||||
}
|
||||
wsClient.unsubscribeToWorkspace(match.params.workspaceId || '0')
|
||||
if (subscribedToWorkspace) {
|
||||
wsClient.unsubscribeToWorkspace(match.params.workspaceId || '0')
|
||||
}
|
||||
wsClient.removeOnChange(incrementalUpdate)
|
||||
wsClient.removeOnReconnect(() => dispatch(loadAction(match.params.boardId)))
|
||||
wsClient.removeOnStateChange(updateWebsocketState)
|
||||
}
|
||||
}, [match.params.workspaceId, props.readonly])
|
||||
}, [match.params.workspaceId, props.readonly, match.params.boardId])
|
||||
|
||||
useHotkeys('ctrl+z,cmd+z', () => {
|
||||
Utils.log('Undo')
|
||||
|
@ -1,27 +1,28 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"jsx": "react",
|
||||
"target": "es2019",
|
||||
"module": "commonjs",
|
||||
"esModuleInterop": true,
|
||||
"noImplicitAny": true,
|
||||
"strict": true,
|
||||
"strictNullChecks": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"sourceMap": true,
|
||||
"allowJs": true,
|
||||
"resolveJsonModule": true,
|
||||
"incremental": false,
|
||||
"outDir": "./dist",
|
||||
"moduleResolution": "node"
|
||||
},
|
||||
"include": [
|
||||
"."
|
||||
],
|
||||
"exclude": [
|
||||
"compilerOptions": {
|
||||
"jsx": "react",
|
||||
"target": "es2019",
|
||||
"module": "commonjs",
|
||||
"esModuleInterop": true,
|
||||
"noImplicitAny": true,
|
||||
"strict": true,
|
||||
"strictNullChecks": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"sourceMap": true,
|
||||
"allowJs": true,
|
||||
"resolveJsonModule": true,
|
||||
"incremental": false,
|
||||
"baseUrl": "src",
|
||||
"outDir": "./dist",
|
||||
"moduleResolution": "node"
|
||||
},
|
||||
"include": [
|
||||
"."
|
||||
],
|
||||
"exclude": [
|
||||
".git",
|
||||
"**/node_modules/*",
|
||||
"dist",
|
||||
"pack"
|
||||
"**/node_modules/*",
|
||||
"dist",
|
||||
"pack"
|
||||
]
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user