From 4a05b7c8d50d8d644a4e3bc825bb2855fd478040 Mon Sep 17 00:00:00 2001 From: Devin Binnie <52460000+devinbinnie@users.noreply.github.com> Date: Mon, 17 Jan 2022 10:20:11 -0500 Subject: [PATCH] [MM-39852] Setup docker image to run in CI for E2E (#1946) * [MM-39852] Setup docker image to run in CI for E2E * Setup remote docker * Install docker * Trying this * And this * how about this * this * Okay this * dis one * sdfsagsdags * Now? * aaaaaaa * asdasdasd * i am dumb * blank * Please work * Lint fix * Forgot to update a couple things * OOPS * Testing something since this one is still failing * Trying robotjs instead * test * Remove stop docker * Try without the admin user (since apparently turning off admin notices didn't work) * Remove console statement Co-authored-by: Mattermod --- .circleci/config.yml | 15 ++- e2e/modules/environment.js | 115 +++++++++++------- e2e/specs/menu_bar/dropdown.test.js | 4 +- e2e/specs/menu_bar/view_menu.test.js | 8 +- .../edit_server_modal.test.js | 12 +- .../remove_server_modal.test.js | 5 +- e2e/specs/settings.test.js | 2 + e2e/specs/startup/config.test.js | 2 +- package-lock.json | 42 ++++++- package.json | 10 +- scripts/setup_e2e_docker.js | 59 +++++++++ 11 files changed, 205 insertions(+), 69 deletions(-) create mode 100644 scripts/setup_e2e_docker.js diff --git a/.circleci/config.yml b/.circleci/config.yml index 0cbddf2f..83cb7340 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -9,6 +9,13 @@ orbs: owasp: entur/owasp@0.0.10 executors: + check-image: + working_directory: ~/mattermost-desktop + docker: + - image: electronuserland/builder:wine-chrome + environment: + TAR_OPTIONS: --no-same-owner + - image: mattermost/mattermost-preview wine-chrome: working_directory: ~/mattermost-desktop docker: @@ -127,12 +134,18 @@ commands: path: /tmp/artifacts jobs: check: - executor: wine-chrome + executor: check-image steps: - checkout - update_image: apt_opts: "--no-install-recommends" + - run: wget https://github.com/mattermost/mmctl/releases/download/v6.0.0/linux_amd64.tar && tar -xvf linux_amd64.tar - run: npm run check-types + - run: + name: Setup MM Docker Image + command: npm run test:docker + environment: + MMCTL_PATH: ./mmctl - run: ELECTRON_DISABLE_SANDBOX=1 xvfb-run npm run test - run: mkdir -p /tmp/test-results - run: cp test-results.xml /tmp/test-results/ diff --git a/e2e/modules/environment.js b/e2e/modules/environment.js index 1dc55fb5..4b6eb829 100644 --- a/e2e/modules/environment.js +++ b/e2e/modules/environment.js @@ -27,55 +27,59 @@ const electronBinaryPath = (() => { const userDataDir = path.join(sourceRootDir, 'e2e/testUserData/'); const configFilePath = path.join(userDataDir, 'config.json'); const boundsInfoPath = path.join(userDataDir, 'bounds-info.json'); -const mattermostURL = 'http://example.com/'; +const exampleURL = 'http://example.com/'; +const mattermostURL = 'http://localhost:8065/'; + +const exampleTeam = { + name: 'example', + url: exampleURL, + order: 0, + tabs: [ + { + name: 'TAB_MESSAGING', + order: 0, + isOpen: true, + }, + { + name: 'TAB_FOCALBOARD', + order: 1, + isOpen: true, + }, + { + name: 'TAB_PLAYBOOKS', + order: 2, + isOpen: true, + }, + ], + lastActiveTab: 0, +}; +const githubTeam = { + name: 'github', + url: 'https://github.com/', + order: 1, + tabs: [ + { + name: 'TAB_MESSAGING', + order: 0, + isOpen: true, + }, + { + name: 'TAB_FOCALBOARD', + order: 1, + isOpen: true, + }, + { + name: 'TAB_PLAYBOOKS', + order: 2, + isOpen: true, + }, + ], + lastActiveTab: 0, +}; const demoConfig = { version: 3, - teams: [{ - name: 'example', - url: mattermostURL, - order: 0, - tabs: [ - { - name: 'TAB_MESSAGING', - order: 0, - isOpen: true, - }, - { - name: 'TAB_FOCALBOARD', - order: 1, - isOpen: true, - }, - { - name: 'TAB_PLAYBOOKS', - order: 2, - isOpen: true, - }, - ], - lastActiveTab: 0, - }, { - name: 'github', - url: 'https://github.com/', - order: 1, - tabs: [ - { - name: 'TAB_MESSAGING', - order: 0, - isOpen: true, - }, - { - name: 'TAB_FOCALBOARD', - order: 1, - isOpen: true, - }, - { - name: 'TAB_PLAYBOOKS', - order: 2, - isOpen: true, - }, - ], - lastActiveTab: 0, - }], + teams: [exampleTeam, githubTeam], showTrayIcon: false, trayIconTheme: 'light', minimizeToTray: false, @@ -93,13 +97,23 @@ const demoConfig = { spellCheckerLocales: [], }; +const demoMattermostConfig = { + ...demoConfig, + teams: [{ + ...exampleTeam, + url: mattermostURL, + }, githubTeam], +}; + module.exports = { sourceRootDir, configFilePath, userDataDir, boundsInfoPath, + exampleURL, mattermostURL, demoConfig, + demoMattermostConfig, cleanTestConfig() { [configFilePath, boundsInfoPath].forEach((file) => { @@ -171,6 +185,15 @@ module.exports = { return map; }, + async loginToMattermost(window) { + await window.waitForSelector('#loginId'); + await window.waitForSelector('#loginPassword'); + await window.waitForSelector('#loginButton'); + await window.type('#loginId', 'user-1'); + await window.type('#loginPassword', 'SampleUs@r-1'); + await window.click('#loginButton'); + }, + addClientCommands(client) { client.addCommand('loadSettingsPage', function async() { ipcRenderer.send(SHOW_SETTINGS_WINDOW); diff --git a/e2e/specs/menu_bar/dropdown.test.js b/e2e/specs/menu_bar/dropdown.test.js index cdd8a569..3c8a36cc 100644 --- a/e2e/specs/menu_bar/dropdown.test.js +++ b/e2e/specs/menu_bar/dropdown.test.js @@ -71,7 +71,7 @@ describe('menu_bar/dropdown', function desc() { const mainWindow = await this.app.firstWindow(); const browserWindow = await this.app.browserWindow(mainWindow); - let firstViewIsAttached = await browserWindow.evaluate((window, url) => Boolean(window.getBrowserViews().find((view) => view.webContents.getURL() === url)), env.mattermostURL); + let firstViewIsAttached = await browserWindow.evaluate((window, url) => Boolean(window.getBrowserViews().find((view) => view.webContents.getURL() === url)), env.exampleURL); firstViewIsAttached.should.be.true; let secondViewIsAttached = await browserWindow.evaluate((window) => Boolean(window.getBrowserViews().find((view) => view.webContents.getURL() === 'https://github.com/'))); secondViewIsAttached.should.be.false; @@ -81,7 +81,7 @@ describe('menu_bar/dropdown', function desc() { await mainView.click('.TeamDropdownButton'); await dropdownView.click('.TeamDropdown button.TeamDropdown__button:nth-child(2)'); - firstViewIsAttached = await browserWindow.evaluate((window, url) => Boolean(window.getBrowserViews().find((view) => view.webContents.getURL() === url)), env.mattermostURL); + firstViewIsAttached = await browserWindow.evaluate((window, url) => Boolean(window.getBrowserViews().find((view) => view.webContents.getURL() === url)), env.exampleURL); firstViewIsAttached.should.be.false; secondViewIsAttached = await browserWindow.evaluate((window) => Boolean(window.getBrowserViews().find((view) => view.webContents.getURL() === 'https://github.com/'))); secondViewIsAttached.should.be.true; diff --git a/e2e/specs/menu_bar/view_menu.test.js b/e2e/specs/menu_bar/view_menu.test.js index 54a3d83c..4dc4963e 100644 --- a/e2e/specs/menu_bar/view_menu.test.js +++ b/e2e/specs/menu_bar/view_menu.test.js @@ -23,7 +23,7 @@ async function setupPromise(window, id) { describe('mattermost', function desc() { this.timeout(30000); - const config = env.demoConfig; + const config = env.demoMattermostConfig; beforeEach(async () => { env.cleanDataDir(); @@ -41,14 +41,14 @@ describe('mattermost', function desc() { } }); - // TODO: enable when we have a server to test against - it.skip('MM-T813 Control+F should focus the search bar in Mattermost', async () => { + it('MM-T813 Control+F should focus the search bar in Mattermost', async () => { const loadingScreen = this.app.windows().find((window) => window.url().includes('loadingScreen')); await loadingScreen.waitForSelector('.LoadingScreen', {state: 'hidden'}); const firstServer = this.serverMap[`${config.teams[0].name}___TAB_MESSAGING`].win; await env.loginToMattermost(firstServer); await firstServer.waitForSelector('#searchBox'); - await firstServer.press('body', process.platform === 'darwin' ? 'Meta+F' : 'Control+F'); + robot.keyTap('f', [process.platform === 'darwin' ? 'command' : 'control']); + await asyncSleep(500); const isFocused = await firstServer.$eval('#searchBox', (el) => el === document.activeElement); isFocused.should.be.true; const text = await firstServer.inputValue('#searchBox'); diff --git a/e2e/specs/server_management/edit_server_modal.test.js b/e2e/specs/server_management/edit_server_modal.test.js index ec948045..9459294a 100644 --- a/e2e/specs/server_management/edit_server_modal.test.js +++ b/e2e/specs/server_management/edit_server_modal.test.js @@ -47,7 +47,7 @@ describe('EditServerModal', function desc() { const savedConfig = JSON.parse(fs.readFileSync(env.configFilePath, 'utf8')); savedConfig.teams.should.deep.contain({ name: 'example', - url: env.mattermostURL, + url: env.exampleURL, order: 0, tabs: [ { @@ -79,7 +79,7 @@ describe('EditServerModal', function desc() { const savedConfig = JSON.parse(fs.readFileSync(env.configFilePath, 'utf8')); savedConfig.teams.should.deep.contain({ name: 'example', - url: env.mattermostURL, + url: env.exampleURL, order: 0, tabs: [ { @@ -119,7 +119,7 @@ describe('EditServerModal', function desc() { const savedConfig = JSON.parse(fs.readFileSync(env.configFilePath, 'utf8')); savedConfig.teams.should.not.deep.contain({ name: 'example', - url: env.mattermostURL, + url: env.exampleURL, order: 0, tabs: [ { @@ -142,7 +142,7 @@ describe('EditServerModal', function desc() { }); savedConfig.teams.should.deep.contain({ name: 'NewTestTeam', - url: env.mattermostURL, + url: env.exampleURL, order: 0, tabs: [ { @@ -175,7 +175,7 @@ describe('EditServerModal', function desc() { const savedConfig = JSON.parse(fs.readFileSync(env.configFilePath, 'utf8')); savedConfig.teams.should.not.deep.contain({ name: 'example', - url: env.mattermostURL, + url: env.exampleURL, order: 0, tabs: [ { @@ -233,7 +233,7 @@ describe('EditServerModal', function desc() { const savedConfig = JSON.parse(fs.readFileSync(env.configFilePath, 'utf8')); savedConfig.teams.should.not.deep.contain({ name: 'example', - url: env.mattermostURL, + url: env.exampleURL, order: 0, tabs: [ { diff --git a/e2e/specs/server_management/remove_server_modal.test.js b/e2e/specs/server_management/remove_server_modal.test.js index 11233cc7..3e9b118e 100644 --- a/e2e/specs/server_management/remove_server_modal.test.js +++ b/e2e/specs/server_management/remove_server_modal.test.js @@ -70,7 +70,10 @@ describe('RemoveServerModal', function desc() { }); it('MM-T4390_4 should disappear on click background', async () => { - await removeServerView.click('.modal', {position: {x: 20, y: 20}}); + // ignore any target closed error + try { + await removeServerView.click('.modal', {position: {x: 20, y: 20}}); + } catch {} // eslint-disable-line no-empty await asyncSleep(1000); const existing = Boolean(await this.app.windows().find((window) => window.url().includes('removeServer'))); existing.should.be.false; diff --git a/e2e/specs/settings.test.js b/e2e/specs/settings.test.js index 35d53fb8..4b814aac 100644 --- a/e2e/specs/settings.test.js +++ b/e2e/specs/settings.test.js @@ -41,6 +41,7 @@ describe('Settings', function desc() { predicate: (window) => window.url().includes('settings'), }); await settingsWindow.waitForSelector('.settingsPage.container'); + await settingsWindow.waitForSelector('#inputAutoStart'); const existing = await settingsWindow.isVisible('#inputAutoStart'); existing.should.equal(expected); }); @@ -56,6 +57,7 @@ describe('Settings', function desc() { predicate: (window) => window.url().includes('settings'), }); await settingsWindow.waitForSelector('.settingsPage.container'); + await settingsWindow.waitForSelector('#inputShowTrayIcon'); const existing = await settingsWindow.isVisible('#inputShowTrayIcon'); existing.should.equal(expected); }); diff --git a/e2e/specs/startup/config.test.js b/e2e/specs/startup/config.test.js index 69ae045e..7333352d 100644 --- a/e2e/specs/startup/config.test.js +++ b/e2e/specs/startup/config.test.js @@ -60,7 +60,7 @@ describe('config', function desc() { const Config = require('../../../src/common/config').Config; const newConfig = new Config(env.configFilePath); const oldConfig = { - url: env.mattermostURL, + url: env.exampleURL, }; fs.writeFileSync(env.configFilePath, JSON.stringify(oldConfig)); this.app = await env.getApp(); diff --git a/package-lock.json b/package-lock.json index 8aaeaa4a..9cecc07d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -60,6 +60,7 @@ "@typescript-eslint/parser": "4.28.0", "7zip-bin": "^4.1.0", "awesome-node-loader": "^1.1.1", + "axios": "^0.24.0", "babel-eslint": "^10.0.3", "babel-loader": "^8.0.4", "chai": "^4.2.0", @@ -8114,6 +8115,15 @@ "loader-utils": "^1.1.0" } }, + "node_modules/axios": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz", + "integrity": "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==", + "dev": true, + "dependencies": { + "follow-redirects": "^1.14.4" + } + }, "node_modules/babel-code-frame": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", @@ -16307,12 +16317,23 @@ } }, "node_modules/follow-redirects": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.12.1.tgz", - "integrity": "sha512-tmRv0AVuR7ZyouUHLeNSiO6pqulF7dYa3s19c6t+wz9LD69/uSzdMxJ2S91nTI9U3rt/IldxpzMOFejp6f0hjg==", + "version": "1.14.7", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz", + "integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==", "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], "engines": { "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } } }, "node_modules/font-awesome": { @@ -38096,6 +38117,15 @@ "loader-utils": "^1.1.0" } }, + "axios": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz", + "integrity": "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==", + "dev": true, + "requires": { + "follow-redirects": "^1.14.4" + } + }, "babel-code-frame": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", @@ -44736,9 +44766,9 @@ } }, "follow-redirects": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.12.1.tgz", - "integrity": "sha512-tmRv0AVuR7ZyouUHLeNSiO6pqulF7dYa3s19c6t+wz9LD69/uSzdMxJ2S91nTI9U3rt/IldxpzMOFejp6f0hjg==", + "version": "1.14.7", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz", + "integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==", "dev": true }, "font-awesome": { diff --git a/package.json b/package.json index 5cc86025..ae9da8a6 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "watch:main": "node scripts/watch_main_and_preload.js", "watch:renderer": "webpack-dev-server --config webpack.config.renderer.js", "test": "npm-run-all lint:js test:unit test:e2e", + "test:docker": "node scripts/setup_e2e_docker.js", "test:e2e": "cross-env NODE_ENV=test npm-run-all build build-robotjs test:e2e:build test:e2e:run", "test:e2e:nobuild": "cross-env NODE_ENV=test npm-run-all test:e2e:build test:e2e:run", "test:e2e:build": "webpack-cli --bail --config webpack.config.test.js", @@ -75,11 +76,15 @@ "src/common/**/*.ts", "src/main/**/*.ts" ], - "testMatch": ["**/src/**/*.test.js"], + "testMatch": [ + "**/src/**/*.test.js" + ], "globals": { "__HASH_VERSION__": "5.0.0" }, - "setupFiles": ["./src/jestSetup.js"] + "setupFiles": [ + "./src/jestSetup.js" + ] }, "devDependencies": { "@babel/cli": "^7.14.5", @@ -106,6 +111,7 @@ "@typescript-eslint/parser": "4.28.0", "7zip-bin": "^4.1.0", "awesome-node-loader": "^1.1.1", + "axios": "^0.24.0", "babel-eslint": "^10.0.3", "babel-loader": "^8.0.4", "chai": "^4.2.0", diff --git a/scripts/setup_e2e_docker.js b/scripts/setup_e2e_docker.js new file mode 100644 index 00000000..a24aa36d --- /dev/null +++ b/scripts/setup_e2e_docker.js @@ -0,0 +1,59 @@ +// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +const {spawn, exec} = require('child_process'); + +const axios = require('axios'); + +const mmctlPath = process.env.MMCTL_PATH || 'mmctl'; + +const ping = setInterval(async () => { + try { + const pingRequest = await axios.get('http://localhost:8065/api/v4/system/ping'); + if (pingRequest.status === 200) { + const addUserRequest = await axios.post( + 'http://localhost:8065/api/v4/users', + { + email: 'test@test.com', + username: 'admin1', + password: 'Sys@dmin123', + allow_marketing: false, + }); + if (addUserRequest.status === 201) { + clearInterval(ping); + + exec('echo "Sys@dmin123" > passfile', () => { + const mmctlauth = spawn(mmctlPath, ['auth', 'login', 'http://localhost:8065', '--name', 'local-server', '--username', 'admin1', '--password-file', 'passfile']); + mmctlauth.stdout.on('data', (data) => { + console.log(`${data}`); + }); + + mmctlauth.stderr.on('data', (data) => { + console.log(`ERROR: ${data}`); + }); + + mmctlauth.on('close', () => { + const sampledata = spawn(mmctlPath, ['sampledata']); + sampledata.stdout.on('data', (data) => { + console.log(`${data}`); + }); + + sampledata.stderr.on('data', (data) => { + console.log(`ERROR: ${data}`); + }); + + sampledata.on('close', () => { + exec(`${mmctlPath} config set AnnouncementSettings.UserNoticesEnabled false`, (err, stdout, stderr) => { + console.log(err, stdout, stderr); + }); + }); + }); + }); + } + } else { + console.log(`ERROR: Trying to contact server, got ${pingRequest.status}`); + } + } catch { + console.log('waiting for server to respond...'); + } +}, 1000);