Skip to content

Commit

Permalink
Include Playwright, move some videos and some fixes (#464)
Browse files Browse the repository at this point in the history
We can now make a build without signing
E2E draft tests using playwright
Move videos to static folder within renderer
Fix error when cancelling flashing
Initial not very clever USB tests to check how Electron behaves on testing env.
  • Loading branch information
javierguzman authored Jul 21, 2023
1 parent 631378d commit f3a9aa5
Show file tree
Hide file tree
Showing 22 changed files with 725 additions and 34 deletions.
50 changes: 50 additions & 0 deletions .github/workflows/e2e_testing.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: E2E Tests
on:
push:
branches:
- "*"
- "!main"
- "!development"
pull_request:
branches:
- "*"
- "!main"
- "!development"
jobs:
e2e_test:
timeout-minutes: 60
runs-on: ${{ matrix.os }}

strategy:
fail-fast: false
matrix:
include:
- os: macos-12
- os: windows-2019
- os: ubuntu-20.04

steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18
- run: npm install -g yarn
- if: runner.os == 'Linux'
run: sudo apt update && sudo apt install libudev-dev
- name: Cache node_modules
id: cache-node-modules
uses: actions/cache@v3
with:
path: node_modules
key: ${{ runner.os }}-${{ hashFiles('package.json', 'yarn.lock') }}
- name: Install node_modules
if: steps.cache-node-modules.outputs.cache-hit != 'true'
run: yarn install --frozen-lockfile --network-timeout 300000 # sometimes yarn takes time, therefore, we increase the timeout
- name: Run Playwright tests
run: yarn playwright test
- uses: actions/upload-artifact@v3
if: always()
with:
name: playwright-report
path: playwright-report/
retention-days: 30
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,7 @@ dist

#editor's configuration
.vscode
.idea
.idea
/test-results/
/playwright-report/
/playwright/.cache/
79 changes: 79 additions & 0 deletions e2e_tests/flashing.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { _electron as electron, ElectronApplication, test, Page, expect } from "@playwright/test";
import { findLatestBuild, parseElectronApp } from "electron-playwright-helpers";

// this test does not work yet because we need to do something in firmware to avoid having to hold ESC key
test.describe("Testing Bazecor E2E", async () => {
let electronApp: ElectronApplication;
let page: Page;
const outputFolder = "test-results";

test.beforeAll(async () => {
const latestBuild = findLatestBuild();
const appInfo = parseElectronApp(latestBuild);

electronApp = await electron.launch({
args: [appInfo.main],
executablePath: appInfo.executable,
});

electronApp.on("window", async initialPage => {
const filename = initialPage.url()?.split("/").pop();
console.log(`Window opened: ${filename}`);

initialPage.on("pageerror", error => {
console.error(error);
});

initialPage.on("console", msg => {
console.log(msg.text());
});
});
});

test("renders landing page", async () => {
page = await electronApp.firstWindow();
await page.screenshot({ path: `${outputFolder}/landing.png` });
});

test("flashing keyboard", async () => {
page = await electronApp.firstWindow();
const connectButton = page.getByRole("button", { name: "Connect" });

expect(connectButton).not.toBe(undefined);
if (connectButton) {
console.log(connectButton);
await connectButton.click();
await page.screenshot({ path: `${outputFolder}/connected.png` });
}

const firmwareMenuButton = page.getByRole("link", { name: "Firmware Update" });
expect(firmwareMenuButton).not.toBe(undefined);
if (firmwareMenuButton) {
console.log(firmwareMenuButton);
await firmwareMenuButton.click();
await page.screenshot({ path: `${outputFolder}/firmwareUpdateSection.png` });
}

const updateButton = page.getByRole("button", { name: "Update now" });
expect(updateButton).not.toBe(undefined);
if (updateButton) {
console.log(updateButton);
await updateButton.click();
await page.waitForTimeout(1000);
await page.screenshot({ path: `${outputFolder}/updateStart.png` });
}

// we start flashing procedure
await page.waitForSelector("text=/Press and hold/");
await page.keyboard.down("Escape");
await page.waitForSelector("text=/Updating/");
await page.keyboard.up("Escape");
await page.screenshot({ path: `${outputFolder}/flashing.png` });
await page.waitForSelector("text=/Firmware update!/");
await page.screenshot({ path: `${outputFolder}/flashCompleted.png` });
});

test.afterAll(async () => {
await electronApp.close();
});
});
47 changes: 26 additions & 21 deletions forge.config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { ForgeConfig } from "@electron-forge/shared-types";
import type { ForgeConfig, ForgePackagerOptions } from "@electron-forge/shared-types";
import { MakerSquirrel } from "@electron-forge/maker-squirrel";
import { WebpackPlugin } from "@electron-forge/plugin-webpack";
import fs from "fs";
Expand All @@ -8,27 +8,32 @@ import { spawnSync } from "child_process";
import rendererConfig from "./webpack.renderer.config";
import mainConfig from "./webpack.main.config";

const packagerConfig: ForgePackagerOptions = {
appBundleId: "com.dygmalab.bazecor",
darwinDarkModeSupport: true,
icon: "./build/logo.png",
name: "Bazecor",
extraResource: ["NEWS.md"],
appCopyright: "Copyright © 2018, 2023 Keyboardio Inc.; Copyright © 2018, 2023 DygmaLab SE; distributed under the GPLv3",
};

if (process.env.NODE_ENV !== "development") {
packagerConfig.osxNotarize = {
tool: "notarytool",
appleId: process.env.APPLE_ID || "",
appleIdPassword: process.env.APPLE_ID_PASSWORD || "",
teamId: process.env.APPLE_TEAM_ID || "",
};
packagerConfig.osxSign = {
optionsForFile: () => ({
entitlements: "./build/entitlements.plist",
hardenedRuntime: true,
}),
};
}

const config: ForgeConfig = {
packagerConfig: {
appBundleId: "com.dygmalab.bazecor",
darwinDarkModeSupport: true,
icon: "./build/logo.png",
name: "Bazecor",
extraResource: ["NEWS.md"],
appCopyright: "Copyright © 2018, 2023 Keyboardio Inc.; Copyright © 2018, 2023 DygmaLab SE; distributed under the GPLv3",
osxNotarize: {
tool: "notarytool",
appleId: process.env.APPLE_ID || "",
appleIdPassword: process.env.APPLE_ID_PASSWORD || "",
teamId: process.env.APPLE_TEAM_ID || "",
},
osxSign: {
optionsForFile: () => ({
entitlements: "./build/entitlements.plist",
hardenedRuntime: true,
}),
},
},
packagerConfig,
rebuildConfig: {},
makers: [
new MakerSquirrel({
Expand Down
8 changes: 6 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,17 @@
"main": ".webpack/main",
"scripts": {
"clean": "rm -rf dist && rm -rf out && rm -rf node_modules",
"start": "electron-forge start",
"start": "NODE_ENV=development electron-forge start",
"make-dev": "NODE_ENV=development electron-forge make",
"package": "electron-forge package",
"make": "electron-forge make",
"make-win": "electron-forge make --platform win32",
"make-mac": "electron-forge make --platform darwin",
"make-lin": "electron-forge make --platform linux",
"lint": "eslint --ext .ts,.tsx,.js,.jsx .",
"prettier": "prettier --write \"./**/*.{js,json,css,scss,md}\"",
"test": "vitest run",
"test": "vitest run src/renderer/tests src/main/tests",
"e2e": "yarn playwright test",
"commit": "cz",
"prepare": "husky install"
},
Expand Down Expand Up @@ -55,6 +57,7 @@
"@electron-forge/maker-rpm": "^6.1.1",
"@electron-forge/maker-squirrel": "^6.1.1",
"@electron-forge/plugin-webpack": "^6.1.1",
"@playwright/test": "^1.36.0",
"@reforged/maker-appimage": "^3.3.0",
"@semantic-release/changelog": "^6.0.3",
"@semantic-release/commit-analyzer": "^10.0.1",
Expand All @@ -75,6 +78,7 @@
"electron": "^25.2.0",
"electron-devtools-installer": "^3.2.0",
"electron-forge-maker-appimage": "^24.5.2",
"electron-playwright-helpers": "^1.6.0",
"eslint": "^8.0.1",
"eslint-config-airbnb": "^19.0.4",
"eslint-config-airbnb-typescript": "^17.0.0",
Expand Down
67 changes: 67 additions & 0 deletions playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { defineConfig, devices } from "@playwright/test";

/**
* Read environment variables from file.
* https://github.com/motdotla/dotenv
*/
// require('dotenv').config();

/**
* See https://playwright.dev/docs/test-configuration.
*/
export default defineConfig({
testDir: "./e2e_tests",
/* Run tests in files in parallel */
fullyParallel: true,
/* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: !!process.env.CI,
/* Retry on CI only */
retries: process.env.CI ? 2 : 0,
/* Opt out of parallel tests on CI. */
workers: process.env.CI ? 1 : undefined,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: "html",
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Base URL to use in actions like `await page.goto('/')`. */
// baseURL: 'http://127.0.0.1:3000',

/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: "on-first-retry",
},

/* Configure projects for major browsers */
projects: [
{
name: "chromium",
use: { ...devices["Desktop Chrome"] },
},

/* Test against mobile viewports. */
// {
// name: 'Mobile Chrome',
// use: { ...devices['Pixel 5'] },
// },
// {
// name: 'Mobile Safari',
// use: { ...devices['iPhone 12'] },
// },

/* Test against branded browsers. */
// {
// name: 'Microsoft Edge',
// use: { ...devices['Desktop Edge'], channel: 'msedge' },
// },
// {
// name: 'Google Chrome',
// use: { ...devices['Desktop Chrome'], channel: 'chrome' },
// },
],

/* Run your local dev server before starting the tests */
// webServer: {
// command: 'npm run start',
// url: 'http://127.0.0.1:3000',
// reuseExistingServer: !process.env.CI,
// },
});
6 changes: 6 additions & 0 deletions src/main/setup/configureUSB.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ export const removeUSBListeners = () => {
webusb.removeEventListener("disconnect", onUSBDisconnect);
};

export const getDevices = () => {
const devices = getDeviceList();
return devices;
};

export const configureUSB = async () => {
// We're relying on webusb to send us notifications about device
// connect/disconnect events, but it only sends disconnect events for devices
Expand All @@ -78,4 +83,5 @@ export const configureUSB = async () => {
const devices = getDeviceList();
return devices;
});

};
18 changes: 18 additions & 0 deletions src/main/tests/usb.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { test, vi, expect } from "vitest";
import { configureUSB, getDevices } from "../setup/configureUSB";

test("get list of USB devices", async () => {
vi.mock("electron", async () => {
const mockIpcMain = {
handle: vi.fn().mockReturnThis(),
};
return {
ipcMain: mockIpcMain,
};
});
await configureUSB();
const devices = getDevices();
if (!process.env.GITHUB_ACTIONS) {
expect(devices.length).toBeGreaterThan(0);
}
});
2 changes: 1 addition & 1 deletion src/renderer/modules/Firmware/FirmwareErrorPanel.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import { FirmwareLoader } from "../../component/Loader";
import { FirmwareNeuronStatus } from "../Firmware";

//Assets
import videoDefyCablesDisconnect from "../../../../static/videos/connectCablesDefy.mp4";
import videoDefyCablesDisconnect from "@Assets/videos/connectCablesDefy.mp4";
import { IconNoWifi, IconWarning } from "../../component/Icon";

const Style = Styled.div`
Expand Down
12 changes: 6 additions & 6 deletions src/renderer/modules/Firmware/FirmwareImageHelp.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ import Styled from "styled-components";

import { FirmwareNeuronHelp, FirmwareDefyUpdatingStatus } from "../Firmware";

import videoFirmwareUpdate from "../../../../static/videos/update-firmware.mp4";
import videoFirmwareUpdateReleaseKey from "../../../../static/videos/release-key.mp4";
import videoFirmwareUpdateDefySRC from "../../../../static/videos/update-firmware-defy.mp4";
import videoFirmwareUpdateDefyReleaseSRC from "../../../../static/videos/release-key-defy.mp4";
import videoFirmwareUpdate from "@Assets/videos/update-firmware.mp4";
import videoFirmwareUpdateReleaseKey from "@Assets/videos/release-key.mp4";
import videoFirmwareUpdateDefySRC from "@Assets/videos/update-firmware-defy.mp4";
import videoFirmwareUpdateDefyReleaseSRC from "@Assets/videos/release-key-defy.mp4";
import { IconCheckmarkSm } from "../../component/Icon";

const Style = Styled.div`
Expand Down Expand Up @@ -160,9 +160,9 @@ const FirmwareImageHelp = ({
checkSuccess.current.classList.add("animInCheck");
}
return () => {
if (countdown == 0 && deviceProduct == "Raise") {
if (videoIntro.current && countdown == 0 && deviceProduct == "Raise") {
videoIntro.current.removeEventListener("ended", playVideo, false);
} else if (countdown == 0 && deviceProduct == "Defy") {
} else if (videoIntroDefy.current && countdown == 0 && deviceProduct == "Defy") {
videoIntroDefy.current.removeEventListener("ended", playVideo, false);
}
};
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/modules/Firmware/FirmwareWarningList.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import i18n from "../../i18n";

import Title from "../../component/Title";

import videoDefyCablesDisconnect from "../../../../static/videos/connectCablesDefy.mp4";
import videoDefyCablesDisconnect from "@Assets/videos/connectCablesDefy.mp4";

const Style = Styled.div`
.errorListWrapper {
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loading

0 comments on commit f3a9aa5

Please sign in to comment.