Hello:
I’m trying to add some UI test(e2e test) to my Theia application and I found the basic-example.espec.ts of Theia:
// *****************************************************************************
// Copyright (C) 2017 Ericsson and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
// *****************************************************************************
import * as chai from 'chai';
import * as path from 'path';
import { app, BrowserWindow } from 'electron';
const expect = chai.expect;
describe.skip('basic-example-spec', () => {
const mainWindow: Electron.BrowserWindow = new BrowserWindow({ show: false });
mainWindow.on('ready-to-show', () => mainWindow.show());
describe('01 #start example app', () => {
it('should start the electron example app', async () => {
if (!app.isReady()) {
await new Promise(resolve => app.on('ready', resolve));
}
require('../src-gen/backend/main'); // start the express server
mainWindow.webContents.openDevTools();
mainWindow.loadURL(`file://${path.join(__dirname, 'index.html')}`);
// eslint-disable-next-line no-unused-expressions
expect(mainWindow.isVisible()).to.be.true;
});
});
});
But it looks like a demo without a concrete implementation since it use describe.skip()
. Even if I modified the describe.skip()
to describe()
, paths ../src-gen/backend/main
and file://${path.join(__dirname, 'index.html')}
need to be modified to work properly.
Then I found the app.spec.js of Theia-blueprint:
const os = require("os");
const path = require("path");
const { remote } = require("webdriverio");
const { expect } = require("chai");
const THEIA_LOAD_TIMEOUT = 15000; // 15 seconds
function getBinaryPath() {
const distFolder = path.join(__dirname, "..", "dist");
switch (os.platform()) {
case "linux":
return path.join(
distFolder,
"linux-unpacked",
"theia-blueprint"
);
case "win32":
return path.join(
distFolder,
"win-unpacked",
"TheiaBlueprint.exe"
);
case "darwin":
return path.join(
distFolder,
"mac",
"TheiaBlueprint.app",
"Contents",
"MacOS",
"TheiaBlueprint"
);
default:
return undefined;
}
};
// Utility for keyboard shortcuts that execute commands where
// the key combination is the same on all platforms *except that*
// the Command key is used instead of Control on MacOS. Note that
// sometimes MacOS also uses Control. This is not handled, here
function macSafeKeyCombo(keys) {
if (os.platform() === "darwin" && keys.includes("Control")) {
// Puppeteer calls the Command key "Meta"
return keys.map((k) => k === "Control" ? "Meta" : k);
}
return keys;
};
describe("Theia App", function() {
// In mocha, 'this' is a common context between sibling beforeEach, afterEach, it, etc methods within the same describe.
// Each describe has its own context.
beforeEach(async function() {
const binary = getBinaryPath();
if (!binary) {
throw new Error("Tests are not supported for this platform.")
}
// Start app and store connection in context (this)
this.browser = await remote({
// Change to info to get detailed events of webdriverio
logLevel: "info",
capabilities: {
browserName: "chrome",
"goog:chromeOptions": {
// Path to built and packaged theia
binary: binary,
// Hand in workspace to load as runtime parameter
args: [path.join(__dirname, "workspace")],
},
},
});
const appShell = await this.browser.$("#theia-app-shell");
// mocha waits for returned promise to resolve
// Theia is loaded once the app shell is present
return appShell.waitForExist({
timeout: THEIA_LOAD_TIMEOUT,
timeoutMsg: "Theia took too long to load.",
});
});
afterEach(async function() {
try {
await this.browser.closeWindow();
} catch (err) {
// Workaround: Puppeteer cannot properly connect to electron and throws an error.
// However, the window is closed and that's all we want here.
if (`${err}`.includes("Protocol error (Target.createTarget)")) {
return;
}
// Rethrow for unexpected errors to fail test.
throw err;
}
});
it("Correct window title", async function() {
const windowTitle = await this.browser.getTitle();
expect(windowTitle).to.include("workspace — Theia");
});
it("Builtin extensions", async function() {
// Wait a bit to make sure key handlers are registered.
await new Promise((r) => setTimeout(r, 2000));
// Open extensions view
await this.browser.keys(macSafeKeyCombo(["Control", "Shift", "x"]));
const builtinContainer = await this.browser.$(
"#vsx-extensions-view-container--vsx-extensions\\:builtin"
);
// Expand builtin extensions
const builtinHeader = await builtinContainer.$(".theia-header.header");
await builtinHeader.click();
// Wait for expansion to finish
const builtin = await this.browser.$(
"#vsx-extensions\\:builtin .theia-TreeContainer"
);
await builtin.waitForExist();
// Get names of all builtin extensions
const extensions = await builtin.$$(".theia-vsx-extension .name");
const extensionNames = await Promise.all(
extensions.map((e) => e.getText())
);
// Exemplary check a few extensions
expect(extensionNames).to.include("Debugger for Java");
expect(extensionNames).to.include("TypeScript Language Basics (built-in)");
});
});
It uses the webdriverio
but the getBinaryPath
function means that the test can only work in production mode and each time I made a change to the source code, I have to package the application before run the test.
Is there any way to implement e2e tests in development mode?