[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 <mattermod@users.noreply.github.com>
This commit is contained in:
Devin Binnie 2022-01-17 10:20:11 -05:00 committed by GitHub
parent 6e8455e80e
commit 4a05b7c8d5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 205 additions and 69 deletions

View file

@ -9,6 +9,13 @@ orbs:
owasp: entur/owasp@0.0.10 owasp: entur/owasp@0.0.10
executors: 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: wine-chrome:
working_directory: ~/mattermost-desktop working_directory: ~/mattermost-desktop
docker: docker:
@ -127,12 +134,18 @@ commands:
path: /tmp/artifacts path: /tmp/artifacts
jobs: jobs:
check: check:
executor: wine-chrome executor: check-image
steps: steps:
- checkout - checkout
- update_image: - update_image:
apt_opts: "--no-install-recommends" 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: 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: ELECTRON_DISABLE_SANDBOX=1 xvfb-run npm run test
- run: mkdir -p /tmp/test-results - run: mkdir -p /tmp/test-results
- run: cp test-results.xml /tmp/test-results/ - run: cp test-results.xml /tmp/test-results/

View file

@ -27,13 +27,12 @@ const electronBinaryPath = (() => {
const userDataDir = path.join(sourceRootDir, 'e2e/testUserData/'); const userDataDir = path.join(sourceRootDir, 'e2e/testUserData/');
const configFilePath = path.join(userDataDir, 'config.json'); const configFilePath = path.join(userDataDir, 'config.json');
const boundsInfoPath = path.join(userDataDir, 'bounds-info.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 demoConfig = { const exampleTeam = {
version: 3,
teams: [{
name: 'example', name: 'example',
url: mattermostURL, url: exampleURL,
order: 0, order: 0,
tabs: [ tabs: [
{ {
@ -53,7 +52,8 @@ const demoConfig = {
}, },
], ],
lastActiveTab: 0, lastActiveTab: 0,
}, { };
const githubTeam = {
name: 'github', name: 'github',
url: 'https://github.com/', url: 'https://github.com/',
order: 1, order: 1,
@ -75,7 +75,11 @@ const demoConfig = {
}, },
], ],
lastActiveTab: 0, lastActiveTab: 0,
}], };
const demoConfig = {
version: 3,
teams: [exampleTeam, githubTeam],
showTrayIcon: false, showTrayIcon: false,
trayIconTheme: 'light', trayIconTheme: 'light',
minimizeToTray: false, minimizeToTray: false,
@ -93,13 +97,23 @@ const demoConfig = {
spellCheckerLocales: [], spellCheckerLocales: [],
}; };
const demoMattermostConfig = {
...demoConfig,
teams: [{
...exampleTeam,
url: mattermostURL,
}, githubTeam],
};
module.exports = { module.exports = {
sourceRootDir, sourceRootDir,
configFilePath, configFilePath,
userDataDir, userDataDir,
boundsInfoPath, boundsInfoPath,
exampleURL,
mattermostURL, mattermostURL,
demoConfig, demoConfig,
demoMattermostConfig,
cleanTestConfig() { cleanTestConfig() {
[configFilePath, boundsInfoPath].forEach((file) => { [configFilePath, boundsInfoPath].forEach((file) => {
@ -171,6 +185,15 @@ module.exports = {
return map; 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) { addClientCommands(client) {
client.addCommand('loadSettingsPage', function async() { client.addCommand('loadSettingsPage', function async() {
ipcRenderer.send(SHOW_SETTINGS_WINDOW); ipcRenderer.send(SHOW_SETTINGS_WINDOW);

View file

@ -71,7 +71,7 @@ describe('menu_bar/dropdown', function desc() {
const mainWindow = await this.app.firstWindow(); const mainWindow = await this.app.firstWindow();
const browserWindow = await this.app.browserWindow(mainWindow); 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; firstViewIsAttached.should.be.true;
let secondViewIsAttached = await browserWindow.evaluate((window) => Boolean(window.getBrowserViews().find((view) => view.webContents.getURL() === 'https://github.com/'))); let secondViewIsAttached = await browserWindow.evaluate((window) => Boolean(window.getBrowserViews().find((view) => view.webContents.getURL() === 'https://github.com/')));
secondViewIsAttached.should.be.false; secondViewIsAttached.should.be.false;
@ -81,7 +81,7 @@ describe('menu_bar/dropdown', function desc() {
await mainView.click('.TeamDropdownButton'); await mainView.click('.TeamDropdownButton');
await dropdownView.click('.TeamDropdown button.TeamDropdown__button:nth-child(2)'); 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; firstViewIsAttached.should.be.false;
secondViewIsAttached = await browserWindow.evaluate((window) => Boolean(window.getBrowserViews().find((view) => view.webContents.getURL() === 'https://github.com/'))); secondViewIsAttached = await browserWindow.evaluate((window) => Boolean(window.getBrowserViews().find((view) => view.webContents.getURL() === 'https://github.com/')));
secondViewIsAttached.should.be.true; secondViewIsAttached.should.be.true;

View file

@ -23,7 +23,7 @@ async function setupPromise(window, id) {
describe('mattermost', function desc() { describe('mattermost', function desc() {
this.timeout(30000); this.timeout(30000);
const config = env.demoConfig; const config = env.demoMattermostConfig;
beforeEach(async () => { beforeEach(async () => {
env.cleanDataDir(); env.cleanDataDir();
@ -41,14 +41,14 @@ describe('mattermost', function desc() {
} }
}); });
// TODO: enable when we have a server to test against it('MM-T813 Control+F should focus the search bar in Mattermost', async () => {
it.skip('MM-T813 Control+F should focus the search bar in Mattermost', async () => {
const loadingScreen = this.app.windows().find((window) => window.url().includes('loadingScreen')); const loadingScreen = this.app.windows().find((window) => window.url().includes('loadingScreen'));
await loadingScreen.waitForSelector('.LoadingScreen', {state: 'hidden'}); await loadingScreen.waitForSelector('.LoadingScreen', {state: 'hidden'});
const firstServer = this.serverMap[`${config.teams[0].name}___TAB_MESSAGING`].win; const firstServer = this.serverMap[`${config.teams[0].name}___TAB_MESSAGING`].win;
await env.loginToMattermost(firstServer); await env.loginToMattermost(firstServer);
await firstServer.waitForSelector('#searchBox'); 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); const isFocused = await firstServer.$eval('#searchBox', (el) => el === document.activeElement);
isFocused.should.be.true; isFocused.should.be.true;
const text = await firstServer.inputValue('#searchBox'); const text = await firstServer.inputValue('#searchBox');

View file

@ -47,7 +47,7 @@ describe('EditServerModal', function desc() {
const savedConfig = JSON.parse(fs.readFileSync(env.configFilePath, 'utf8')); const savedConfig = JSON.parse(fs.readFileSync(env.configFilePath, 'utf8'));
savedConfig.teams.should.deep.contain({ savedConfig.teams.should.deep.contain({
name: 'example', name: 'example',
url: env.mattermostURL, url: env.exampleURL,
order: 0, order: 0,
tabs: [ tabs: [
{ {
@ -79,7 +79,7 @@ describe('EditServerModal', function desc() {
const savedConfig = JSON.parse(fs.readFileSync(env.configFilePath, 'utf8')); const savedConfig = JSON.parse(fs.readFileSync(env.configFilePath, 'utf8'));
savedConfig.teams.should.deep.contain({ savedConfig.teams.should.deep.contain({
name: 'example', name: 'example',
url: env.mattermostURL, url: env.exampleURL,
order: 0, order: 0,
tabs: [ tabs: [
{ {
@ -119,7 +119,7 @@ describe('EditServerModal', function desc() {
const savedConfig = JSON.parse(fs.readFileSync(env.configFilePath, 'utf8')); const savedConfig = JSON.parse(fs.readFileSync(env.configFilePath, 'utf8'));
savedConfig.teams.should.not.deep.contain({ savedConfig.teams.should.not.deep.contain({
name: 'example', name: 'example',
url: env.mattermostURL, url: env.exampleURL,
order: 0, order: 0,
tabs: [ tabs: [
{ {
@ -142,7 +142,7 @@ describe('EditServerModal', function desc() {
}); });
savedConfig.teams.should.deep.contain({ savedConfig.teams.should.deep.contain({
name: 'NewTestTeam', name: 'NewTestTeam',
url: env.mattermostURL, url: env.exampleURL,
order: 0, order: 0,
tabs: [ tabs: [
{ {
@ -175,7 +175,7 @@ describe('EditServerModal', function desc() {
const savedConfig = JSON.parse(fs.readFileSync(env.configFilePath, 'utf8')); const savedConfig = JSON.parse(fs.readFileSync(env.configFilePath, 'utf8'));
savedConfig.teams.should.not.deep.contain({ savedConfig.teams.should.not.deep.contain({
name: 'example', name: 'example',
url: env.mattermostURL, url: env.exampleURL,
order: 0, order: 0,
tabs: [ tabs: [
{ {
@ -233,7 +233,7 @@ describe('EditServerModal', function desc() {
const savedConfig = JSON.parse(fs.readFileSync(env.configFilePath, 'utf8')); const savedConfig = JSON.parse(fs.readFileSync(env.configFilePath, 'utf8'));
savedConfig.teams.should.not.deep.contain({ savedConfig.teams.should.not.deep.contain({
name: 'example', name: 'example',
url: env.mattermostURL, url: env.exampleURL,
order: 0, order: 0,
tabs: [ tabs: [
{ {

View file

@ -70,7 +70,10 @@ describe('RemoveServerModal', function desc() {
}); });
it('MM-T4390_4 should disappear on click background', async () => { it('MM-T4390_4 should disappear on click background', async () => {
// ignore any target closed error
try {
await removeServerView.click('.modal', {position: {x: 20, y: 20}}); await removeServerView.click('.modal', {position: {x: 20, y: 20}});
} catch {} // eslint-disable-line no-empty
await asyncSleep(1000); await asyncSleep(1000);
const existing = Boolean(await this.app.windows().find((window) => window.url().includes('removeServer'))); const existing = Boolean(await this.app.windows().find((window) => window.url().includes('removeServer')));
existing.should.be.false; existing.should.be.false;

View file

@ -41,6 +41,7 @@ describe('Settings', function desc() {
predicate: (window) => window.url().includes('settings'), predicate: (window) => window.url().includes('settings'),
}); });
await settingsWindow.waitForSelector('.settingsPage.container'); await settingsWindow.waitForSelector('.settingsPage.container');
await settingsWindow.waitForSelector('#inputAutoStart');
const existing = await settingsWindow.isVisible('#inputAutoStart'); const existing = await settingsWindow.isVisible('#inputAutoStart');
existing.should.equal(expected); existing.should.equal(expected);
}); });
@ -56,6 +57,7 @@ describe('Settings', function desc() {
predicate: (window) => window.url().includes('settings'), predicate: (window) => window.url().includes('settings'),
}); });
await settingsWindow.waitForSelector('.settingsPage.container'); await settingsWindow.waitForSelector('.settingsPage.container');
await settingsWindow.waitForSelector('#inputShowTrayIcon');
const existing = await settingsWindow.isVisible('#inputShowTrayIcon'); const existing = await settingsWindow.isVisible('#inputShowTrayIcon');
existing.should.equal(expected); existing.should.equal(expected);
}); });

View file

@ -60,7 +60,7 @@ describe('config', function desc() {
const Config = require('../../../src/common/config').Config; const Config = require('../../../src/common/config').Config;
const newConfig = new Config(env.configFilePath); const newConfig = new Config(env.configFilePath);
const oldConfig = { const oldConfig = {
url: env.mattermostURL, url: env.exampleURL,
}; };
fs.writeFileSync(env.configFilePath, JSON.stringify(oldConfig)); fs.writeFileSync(env.configFilePath, JSON.stringify(oldConfig));
this.app = await env.getApp(); this.app = await env.getApp();

42
package-lock.json generated
View file

@ -60,6 +60,7 @@
"@typescript-eslint/parser": "4.28.0", "@typescript-eslint/parser": "4.28.0",
"7zip-bin": "^4.1.0", "7zip-bin": "^4.1.0",
"awesome-node-loader": "^1.1.1", "awesome-node-loader": "^1.1.1",
"axios": "^0.24.0",
"babel-eslint": "^10.0.3", "babel-eslint": "^10.0.3",
"babel-loader": "^8.0.4", "babel-loader": "^8.0.4",
"chai": "^4.2.0", "chai": "^4.2.0",
@ -8114,6 +8115,15 @@
"loader-utils": "^1.1.0" "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": { "node_modules/babel-code-frame": {
"version": "6.26.0", "version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
@ -16307,12 +16317,23 @@
} }
}, },
"node_modules/follow-redirects": { "node_modules/follow-redirects": {
"version": "1.12.1", "version": "1.14.7",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.12.1.tgz", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz",
"integrity": "sha512-tmRv0AVuR7ZyouUHLeNSiO6pqulF7dYa3s19c6t+wz9LD69/uSzdMxJ2S91nTI9U3rt/IldxpzMOFejp6f0hjg==", "integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==",
"dev": true, "dev": true,
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"engines": { "engines": {
"node": ">=4.0" "node": ">=4.0"
},
"peerDependenciesMeta": {
"debug": {
"optional": true
}
} }
}, },
"node_modules/font-awesome": { "node_modules/font-awesome": {
@ -38096,6 +38117,15 @@
"loader-utils": "^1.1.0" "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": { "babel-code-frame": {
"version": "6.26.0", "version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
@ -44736,9 +44766,9 @@
} }
}, },
"follow-redirects": { "follow-redirects": {
"version": "1.12.1", "version": "1.14.7",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.12.1.tgz", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz",
"integrity": "sha512-tmRv0AVuR7ZyouUHLeNSiO6pqulF7dYa3s19c6t+wz9LD69/uSzdMxJ2S91nTI9U3rt/IldxpzMOFejp6f0hjg==", "integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==",
"dev": true "dev": true
}, },
"font-awesome": { "font-awesome": {

View file

@ -38,6 +38,7 @@
"watch:main": "node scripts/watch_main_and_preload.js", "watch:main": "node scripts/watch_main_and_preload.js",
"watch:renderer": "webpack-dev-server --config webpack.config.renderer.js", "watch:renderer": "webpack-dev-server --config webpack.config.renderer.js",
"test": "npm-run-all lint:js test:unit test:e2e", "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": "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: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", "test:e2e:build": "webpack-cli --bail --config webpack.config.test.js",
@ -75,11 +76,15 @@
"src/common/**/*.ts", "src/common/**/*.ts",
"src/main/**/*.ts" "src/main/**/*.ts"
], ],
"testMatch": ["**/src/**/*.test.js"], "testMatch": [
"**/src/**/*.test.js"
],
"globals": { "globals": {
"__HASH_VERSION__": "5.0.0" "__HASH_VERSION__": "5.0.0"
}, },
"setupFiles": ["./src/jestSetup.js"] "setupFiles": [
"./src/jestSetup.js"
]
}, },
"devDependencies": { "devDependencies": {
"@babel/cli": "^7.14.5", "@babel/cli": "^7.14.5",
@ -106,6 +111,7 @@
"@typescript-eslint/parser": "4.28.0", "@typescript-eslint/parser": "4.28.0",
"7zip-bin": "^4.1.0", "7zip-bin": "^4.1.0",
"awesome-node-loader": "^1.1.1", "awesome-node-loader": "^1.1.1",
"axios": "^0.24.0",
"babel-eslint": "^10.0.3", "babel-eslint": "^10.0.3",
"babel-loader": "^8.0.4", "babel-loader": "^8.0.4",
"chai": "^4.2.0", "chai": "^4.2.0",

View file

@ -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);