mirror of
https://github.com/tiddly-gittly/TidGi-Desktop.git
synced 2026-01-30 04:11:33 -08:00
chore: setup cucumber BBD environment
This commit is contained in:
parent
afe8ecd076
commit
7641376b89
17 changed files with 2685 additions and 144 deletions
30
cucumber.js
Normal file
30
cucumber.js
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
const feature = [
|
||||
'--require-module ts-node/register',
|
||||
'--require features/**/*.ts',
|
||||
`--format progress-bar`,
|
||||
'--format rerun:logs/@rerun.txt',
|
||||
'--format usage:logs/usage.txt',
|
||||
'--format message:logs/messages.ndjson',
|
||||
'--publish-quiet',
|
||||
].join(' ');
|
||||
|
||||
const cck = ['--require-module', 'ts-node/register', '--format', 'message'].join(' ');
|
||||
|
||||
const FORMATTERS_INCLUDE = ['attachments', 'data-tables', 'examples-tables', 'minimal', 'parameter-types', 'rules', 'stack-traces', '--publish-quiet'];
|
||||
|
||||
const htmlFormatter = [
|
||||
`node_modules/@cucumber/compatibility-kit/features/{${FORMATTERS_INCLUDE.join(',')}}/*.feature`,
|
||||
'--require-module',
|
||||
'ts-node/register',
|
||||
'--require',
|
||||
`compatibility/features/{${FORMATTERS_INCLUDE.join(',')}}/*.ts`,
|
||||
'--format',
|
||||
'html:html-formatter.html',
|
||||
'--publish-quiet',
|
||||
].join(' ');
|
||||
|
||||
module.exports = {
|
||||
default: feature,
|
||||
// cck,
|
||||
// htmlFormatter,
|
||||
};
|
||||
8
features/openClose.feature
Normal file
8
features/openClose.feature
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
Feature: Open
|
||||
As a user of TiddlyGit
|
||||
I want to open the app
|
||||
So I can be more productive
|
||||
|
||||
Scenario: Opening TiddlyGit
|
||||
Given the app is launched
|
||||
Then the element "#new-user-tip" is on the page
|
||||
16
features/stepDefinitions/electron.ts
Normal file
16
features/stepDefinitions/electron.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
/* eslint-disable @typescript-eslint/no-unused-expressions */
|
||||
import { setWorldConstructor, Given, Then, When } from '@cucumber/cucumber';
|
||||
import { expect } from 'chai';
|
||||
import { TiddlyGitWorld } from '../supports/world';
|
||||
|
||||
setWorldConstructor(TiddlyGitWorld);
|
||||
|
||||
Given('the app is launched', { timeout: 120 * 1000 }, async function (this: TiddlyGitWorld) {
|
||||
await this.start();
|
||||
});
|
||||
|
||||
Then('the element {string} is on the page', { timeout: 120 * 1000 }, async function (this: TiddlyGitWorld, elementSelector: string) {
|
||||
const result = await this.getElement(elementSelector);
|
||||
expect(result).to.not.be.undefined;
|
||||
this.updateContext({ previousElement: result });
|
||||
});
|
||||
28
features/supports/aftet.ts
Normal file
28
features/supports/aftet.ts
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
/* eslint-disable unicorn/filename-case */
|
||||
import { After, Before, Status } from '@cucumber/cucumber';
|
||||
import jetpack from 'fs-extra';
|
||||
import path from 'path';
|
||||
import { TiddlyGitWorld } from './world';
|
||||
|
||||
Before(function () {
|
||||
// TODO: clear setting folder
|
||||
});
|
||||
|
||||
After(async function (this: TiddlyGitWorld, testCase) {
|
||||
if (this.app !== undefined && testCase.result?.status === Status.FAILED) {
|
||||
console.log('main:\n---\n');
|
||||
await this.app.client.getMainProcessLogs().then(function (logs) {
|
||||
logs.forEach(function (log) {
|
||||
console.log(log, '\n');
|
||||
});
|
||||
});
|
||||
console.log('renderer:\n---\n');
|
||||
await this.app.client.getRenderProcessLogs().then(function (logs) {
|
||||
logs.forEach(function (log) {
|
||||
console.log(JSON.stringify(log), '\n');
|
||||
});
|
||||
});
|
||||
console.log('\n');
|
||||
}
|
||||
return this.close();
|
||||
});
|
||||
91
features/supports/world.ts
Normal file
91
features/supports/world.ts
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
import { setDefaultTimeout, World } from '@cucumber/cucumber';
|
||||
import path from 'path';
|
||||
import { delay } from 'bluebird';
|
||||
import { Application } from 'spectron';
|
||||
import { keyboard, Key } from '@nut-tree/nut-js';
|
||||
|
||||
setDefaultTimeout(30 * 1000);
|
||||
|
||||
const projectRoot = path.join(__dirname, '..', '..');
|
||||
const packageName = process.env.npm_package_name ?? 'TiddlyGit';
|
||||
|
||||
interface IContext {
|
||||
previousElement?: WebdriverIO.Element;
|
||||
}
|
||||
/**
|
||||
* Execution environment for TiddlyGit in cucumber-js
|
||||
*/
|
||||
export class TiddlyGitWorld extends World {
|
||||
/** our electron app instance created by spectron */
|
||||
public app?: Application;
|
||||
/** store selected element and other things, so subsequent cucumber statement can get context */
|
||||
public context?: IContext;
|
||||
|
||||
/** the compiled src/main.ts */
|
||||
private readonly appPath = path.join(projectRoot, '.webpack', 'main', 'index.js');
|
||||
|
||||
/** cold start the electron app */
|
||||
public async start(): Promise<void> {
|
||||
this.app = new Application({
|
||||
path: path.join(
|
||||
projectRoot,
|
||||
// The path to the binary depends on your platform and architecture
|
||||
`out/${packageName}-darwin-x64/${packageName}.app/Contents/MacOS/${packageName}`,
|
||||
),
|
||||
args: [this.appPath],
|
||||
chromeDriverArgs: ['--disable-extensions'],
|
||||
cwd: projectRoot,
|
||||
env: {
|
||||
NODE_ENV: 'test',
|
||||
},
|
||||
port: 9156,
|
||||
});
|
||||
await this.app.start();
|
||||
while (undefined === (await this.getElement('#test'))) {
|
||||
await delay(500);
|
||||
}
|
||||
}
|
||||
|
||||
public async getElement(selector: string): Promise<WebdriverIO.Element | undefined> {
|
||||
const element = await this.app?.client?.$?.(selector);
|
||||
// sometimes element exist, but has an error field
|
||||
/* Element {
|
||||
sessionId: 'ae55dccb0daecda748fa4239f89d03e5',
|
||||
error: {
|
||||
error: 'no such element',
|
||||
message: 'no such element: Unable to locate element: {"method":"css selector","selector":"#test"}\n' +
|
||||
' (Session info: chrome=89.0.4389.114)', */
|
||||
if (element !== undefined && !('error' in element)) {
|
||||
return element;
|
||||
}
|
||||
}
|
||||
|
||||
public updateContext(context: Partial<IContext>): void {
|
||||
this.context = this.context === undefined ? context : { ...this.context, ...context };
|
||||
}
|
||||
|
||||
public async type(input: string): Promise<void> {
|
||||
await keyboard.type(input);
|
||||
}
|
||||
|
||||
public async hitKey(key: Key, modifier?: Key): Promise<void> {
|
||||
if (modifier !== undefined) {
|
||||
await keyboard.pressKey(modifier);
|
||||
await keyboard.pressKey(key);
|
||||
await keyboard.releaseKey(key);
|
||||
await keyboard.releaseKey(modifier);
|
||||
} else {
|
||||
await keyboard.pressKey(key);
|
||||
await keyboard.releaseKey(key);
|
||||
}
|
||||
}
|
||||
|
||||
public async close(): Promise<void> {
|
||||
await this.app?.stop();
|
||||
}
|
||||
|
||||
public readClipboard(): string | undefined {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-call
|
||||
return this.app?.electron?.clipboard?.readText?.();
|
||||
}
|
||||
}
|
||||
2581
package-lock.json
generated
2581
package-lock.json
generated
File diff suppressed because it is too large
Load diff
21
package.json
21
package.json
|
|
@ -1,12 +1,15 @@
|
|||
{
|
||||
"name": "tiddly-git",
|
||||
"name": "TiddlyGit",
|
||||
"productName": "TiddlyGit",
|
||||
"description": "Customizable personal knowledge-base with Github as unlimited storage and blogging platform.",
|
||||
"version": "0.3.6",
|
||||
"scripts": {
|
||||
"start": "NODE_ENV=development electron-forge start",
|
||||
"package": "electron-forge package",
|
||||
"make": "electron-forge make",
|
||||
"start": "npm run clean && cross-env NODE_ENV=development electron-forge start",
|
||||
"test": "npm run clean && cross-env NODE_ENV=test npm run package && npm run test-without-package",
|
||||
"test-without-package": "mkdir -p logs && cross-env NODE_ENV=test cucumber-js",
|
||||
"package": "cross-env NODE_ENV=production electron-forge package",
|
||||
"make": "cross-env NODE_ENV=production electron-forge make",
|
||||
"clean": "rimraf ./out ./settings-dev ./logs",
|
||||
"lint": "eslint ./src --ext js",
|
||||
"lint:fix": "eslint ./src --ext js --fix",
|
||||
"installType": "typesync",
|
||||
|
|
@ -125,6 +128,7 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@authing/sso": "1.8.3",
|
||||
"@cucumber/cucumber": "^7.1.0",
|
||||
"@date-io/date-fns": "2.10.8",
|
||||
"@electron-forge/cli": "6.0.0-beta.54",
|
||||
"@electron-forge/maker-deb": "6.0.0-beta.54",
|
||||
|
|
@ -136,7 +140,9 @@
|
|||
"@material-ui/core": "^5.0.0-alpha.27",
|
||||
"@material-ui/icons": "^5.0.0-alpha.27",
|
||||
"@material-ui/lab": "^5.0.0-alpha.27",
|
||||
"@nut-tree/nut-js": "^1.6.0",
|
||||
"@types/bluebird": "^3.5.33",
|
||||
"@types/chai": "^4.2.16",
|
||||
"@types/circular-dependency-plugin": "^5.0.1",
|
||||
"@types/classnames": "2.2.11",
|
||||
"@types/copy-webpack-plugin": "^6.4.0",
|
||||
|
|
@ -165,13 +171,16 @@
|
|||
"@typescript-eslint/eslint-plugin": "4.18.0",
|
||||
"@typescript-eslint/parser": "4.18.0",
|
||||
"ace-builds": "1.4.12",
|
||||
"chai": "^4.3.4",
|
||||
"circular-dependency-plugin": "^5.2.2",
|
||||
"classnames": "2.2.6",
|
||||
"copy-webpack-plugin": "^6.4.1",
|
||||
"cross-env": "^7.0.3",
|
||||
"csp-html-webpack-plugin": "5.1.0",
|
||||
"css-loader": "^5.0.2",
|
||||
"date-fns": "2.19.0",
|
||||
"electron": "^13.0.0-beta.6",
|
||||
"electron": "^12.0.4",
|
||||
"electron-rebuild": "^2.3.5",
|
||||
"eslint": "7.22.0",
|
||||
"eslint-config-prettier": "8.1.0",
|
||||
"eslint-config-standard": "16.0.2",
|
||||
|
|
@ -202,12 +211,14 @@
|
|||
"rimraf": "^3.0.2",
|
||||
"simplebar": "6.0.0-beta.4",
|
||||
"simplebar-react": "3.0.0-beta.5",
|
||||
"spectron": "^14.0.0",
|
||||
"style-loader": "^2.0.0",
|
||||
"styled-components": "5.2.1",
|
||||
"subscriptions-transport-ws": "^0.9.18",
|
||||
"ts-import-plugin": "^1.6.7",
|
||||
"ts-loader": "^8.0.18",
|
||||
"ts-migrate": "^0.1.16",
|
||||
"ts-node": "^9.1.1",
|
||||
"typeface-roboto": "1.1.13",
|
||||
"typescript": "4.2.3",
|
||||
"typescript-plugin-styled-components": "^1.4.4",
|
||||
|
|
|
|||
4
src/constants/environment.ts
Normal file
4
src/constants/environment.ts
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
import isDevelopment from 'electron-is-dev';
|
||||
|
||||
export const isTest = process.env.NODE_ENV === 'test';
|
||||
export const isDevelopmentOrTest = isDevelopment || isTest;
|
||||
|
|
@ -288,14 +288,14 @@ export default function Main(): JSX.Element {
|
|||
{sidebar === true ? (
|
||||
<>
|
||||
<Arrow image={themeSource === 'dark' ? arrowWhite : arrowBlack} />
|
||||
<Tip>
|
||||
<Tip id="new-user-tip">
|
||||
<Tip2Text>Click</Tip2Text>
|
||||
<Avatar>+</Avatar>
|
||||
<Tip2Text>to get started!</Tip2Text>
|
||||
</Tip>
|
||||
</>
|
||||
) : (
|
||||
<Tip2>
|
||||
<Tip2 id="new-user-tip">
|
||||
<Tip2Text>
|
||||
<span>Click </span>
|
||||
<strong>Workspaces > Add Workspace</strong>
|
||||
|
|
|
|||
6
src/preload/common/test.ts
Normal file
6
src/preload/common/test.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
// @ts-expect-error for spectron https://github.com/electron-userland/spectron#node-integration
|
||||
window.electronRequire = require;
|
||||
delete window.require;
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
import 'reflect-metadata';
|
||||
import { contextBridge, ipcRenderer } from 'electron';
|
||||
|
||||
import './common/test';
|
||||
import './common/i18n';
|
||||
import './common/remote';
|
||||
import './common/authing-postmessage';
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ async function runApp(): Promise<void> {
|
|||
if (window.meta.windowName === WindowNames.preferences && preventClosingWindow) {
|
||||
return;
|
||||
}
|
||||
window.remote.closeCurrentWindow();
|
||||
void window.remote.closeCurrentWindow();
|
||||
})();
|
||||
});
|
||||
}
|
||||
|
|
@ -62,6 +62,7 @@ async function runApp(): Promise<void> {
|
|||
<CssBaseline />
|
||||
<React.Suspense fallback={<div />}>
|
||||
<I18nextProvider i18n={i18n}>
|
||||
<div id="test" data-usage="For spectron automating testing" />
|
||||
<App />
|
||||
</I18nextProvider>
|
||||
</React.Suspense>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { app } from 'electron';
|
||||
import isDev from 'electron-is-dev';
|
||||
import path from 'path';
|
||||
import os from 'os';
|
||||
import { isDevelopmentOrTest } from '@/constants/environment';
|
||||
|
||||
const isMac = process.platform === 'darwin';
|
||||
|
||||
|
|
@ -11,19 +11,21 @@ export const buildResourcePath = path.resolve(sourcePath, '..', 'build-resources
|
|||
|
||||
const REACT_PATH = MAIN_WINDOW_WEBPACK_ENTRY;
|
||||
// .app/Contents/Resources/wiki/
|
||||
const TIDDLYWIKI_TEMPLATE_FOLDER_PATH = isDev ? path.resolve(sourcePath, '..', 'template', 'wiki') : path.resolve(process.resourcesPath, '..', 'wiki');
|
||||
const TIDDLYWIKI_TEMPLATE_FOLDER_PATH = isDevelopmentOrTest
|
||||
? path.resolve(sourcePath, '..', 'template', 'wiki')
|
||||
: path.resolve(process.resourcesPath, '..', 'wiki');
|
||||
const TIDDLERS_PATH = 'tiddlers';
|
||||
const ICON_PATH = isDev ? path.resolve(buildResourcePath, 'icon.png') : `file://${path.resolve(__dirname, '..', 'icon.png')}`;
|
||||
const ICON_PATH = isDevelopmentOrTest ? path.resolve(buildResourcePath, 'icon.png') : `file://${path.resolve(__dirname, '..', 'icon.png')}`;
|
||||
const CHROME_ERROR_PATH = 'chrome-error://chromewebdata/';
|
||||
const LOGIN_REDIRECT_PATH = 'http://localhost:3000/?code=';
|
||||
const DESKTOP_PATH = path.join(os.homedir(), 'Desktop');
|
||||
const LOG_FOLDER = isDev
|
||||
const LOG_FOLDER = isDevelopmentOrTest
|
||||
? path.resolve(sourcePath, '..', 'logs')
|
||||
: isMac
|
||||
? path.resolve(process.resourcesPath, '..', 'logs')
|
||||
: path.resolve(os.homedir(), '.tg-note', 'logs');
|
||||
const SETTINGS_FOLDER = isDev ? path.resolve(sourcePath, '..', 'settings-dev') : path.resolve(app.getPath('userData'), 'settings');
|
||||
const LOCALIZATION_FOLDER = isDev ? path.resolve(sourcePath, '..', 'localization') : path.resolve(process.resourcesPath, 'localization');
|
||||
const SETTINGS_FOLDER = isDevelopmentOrTest ? path.resolve(sourcePath, '..', 'settings-dev') : path.resolve(app.getPath('userData'), 'settings');
|
||||
const LOCALIZATION_FOLDER = isDevelopmentOrTest ? path.resolve(sourcePath, '..', 'localization') : path.resolve(process.resourcesPath, 'localization');
|
||||
|
||||
export {
|
||||
REACT_PATH,
|
||||
|
|
|
|||
|
|
@ -3,10 +3,10 @@ import { Menu, Tray, ipcMain, nativeImage } from 'electron';
|
|||
import windowStateKeeper from 'electron-window-state';
|
||||
import { menubar, Menubar } from 'menubar';
|
||||
import path from 'path';
|
||||
import isDevelopment from 'electron-is-dev';
|
||||
|
||||
import { REACT_PATH, buildResourcePath } from '@services/constants/paths';
|
||||
import { WindowNames } from './WindowProperties';
|
||||
import { isDevelopmentOrTest, isTest } from '@/constants/environment';
|
||||
|
||||
export default async function handleAttachToMenuBar(): Promise<Menubar> {
|
||||
const menubarWindowState = windowStateKeeper({
|
||||
|
|
@ -37,10 +37,10 @@ export default async function handleAttachToMenuBar(): Promise<Menubar> {
|
|||
minHeight: 100,
|
||||
minWidth: 250,
|
||||
webPreferences: {
|
||||
devTools: true,
|
||||
devTools: !isTest,
|
||||
nodeIntegration: false,
|
||||
enableRemoteModule: false,
|
||||
webSecurity: !isDevelopment,
|
||||
webSecurity: !isDevelopmentOrTest,
|
||||
allowRunningInsecureContent: false,
|
||||
contextIsolation: true,
|
||||
preload: MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
/* eslint-disable @typescript-eslint/consistent-type-assertions */
|
||||
import { BrowserWindow, ipcMain, dialog, app, webFrame, clipboard, BrowserWindowConstructorOptions } from 'electron';
|
||||
import isDevelopment from 'electron-is-dev';
|
||||
import { BrowserWindow, ipcMain, dialog, app, clipboard, BrowserWindowConstructorOptions } from 'electron';
|
||||
import { injectable } from 'inversify';
|
||||
import { Menubar } from 'menubar';
|
||||
import windowStateKeeper, { State as windowStateKeeperState } from 'electron-window-state';
|
||||
|
|
@ -20,6 +19,7 @@ import getFromRenderer from '@services/libs/getFromRenderer';
|
|||
import { lazyInject } from '@services/container';
|
||||
import handleAttachToMenuBar from './handleAttachToMenuBar';
|
||||
import { IWindowService } from './interface';
|
||||
import { isDevelopmentOrTest, isTest } from '@/constants/environment';
|
||||
|
||||
@injectable()
|
||||
export class Window implements IWindowService {
|
||||
|
|
@ -154,10 +154,10 @@ export class Window implements IWindowService {
|
|||
autoHideMenuBar: false,
|
||||
titleBarStyle: titleBar ? 'default' : 'hidden',
|
||||
webPreferences: {
|
||||
devTools: true,
|
||||
devTools: !isTest,
|
||||
nodeIntegration: false,
|
||||
enableRemoteModule: false,
|
||||
webSecurity: !isDevelopment,
|
||||
webSecurity: !isDevelopmentOrTest,
|
||||
allowRunningInsecureContent: false,
|
||||
contextIsolation: true,
|
||||
preload: MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY,
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
"src/**/*.jsx",
|
||||
"public/**/*.ts",
|
||||
"public/**/*.js",
|
||||
"features/**/*.ts",
|
||||
"src/**/*.d.ts",
|
||||
"public/**/*.d.ts",
|
||||
"./*.json",
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
{
|
||||
"exclude": ["template/**/*.js"],
|
||||
"include": ["src", "features"],
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./src",
|
||||
"baseUrl": "./",
|
||||
"paths": {
|
||||
"@services/*": ["./services/*"],
|
||||
"@/*": ["./*"]
|
||||
"@services/*": ["./src/services/*"],
|
||||
"@/*": ["./src/*"]
|
||||
},
|
||||
/* Basic Options */
|
||||
"target": "es2019" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue