Replace UI module with Next.js
All checks were successful
forgejo/Procyon/seedling/pipeline/pr-master This commit looks good
forgejo/Procyon/seedling/pipeline/head This commit looks good

This commit is contained in:
Roman Jaroš 2023-12-28 21:58:08 +00:00
parent 8aeff18162
commit 616205fe73
91 changed files with 3570 additions and 722 deletions

83
source/selenium/.eslintrc Normal file
View file

@ -0,0 +1,83 @@
{
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"prettier"
],
"plugins": [
"typescript",
"@typescript-eslint",
"typescript-sort-keys",
"simple-import-sort",
"import"
],
"settings": {
"import/parsers": {
"@typescript-eslint/parser": [
".ts",
".tsx"
]
}
},
"overrides": [
{
"files": [
"*.js"
],
"parser": "esprima",
"rules": {
"@typescript-eslint/no-var-requires": 0
}
}
],
"ignorePatterns": [],
"rules": {
"max-len": "off",
"no-useless-escape": "off",
"object-curly-spacing": [
"error",
"always"
],
"no-multi-spaces": "error",
"no-console": [
"error",
{
"allow": [
"info",
"warn",
"error"
]
}
],
"no-unused-vars": "off",
"@typescript-eslint/no-non-null-assertion": "off",
"@typescript-eslint/no-unused-vars": 2,
"@typescript-eslint/interface-name-prefix": 0,
"@typescript-eslint/no-empty-interface:": 0,
"@typescript-eslint/explicit-function-return-type": 0,
"@typescript-eslint/explicit-member-accessibility": 2,
"@typescript-eslint/no-explicit-any": 0,
"@typescript-eslint/no-empty-function": 0,
"typescript-sort-keys/interface": "error",
"typescript-sort-keys/string-enum": "error",
"sort-imports": "off",
"import/no-duplicates": "error",
"import/order": "off",
"simple-import-sort/imports": [
"error",
{
"groups": [
[
"^\\u0000"
],
[
"^[^.]"
],
[
"^\\."
]
]
}
]
}
}

View file

@ -0,0 +1,14 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"module": "commonjs",
"types": [
"./global.d.ts"
]
},
"include": [
"**/*.ts"
]
}

View file

@ -0,0 +1,6 @@
# Cucumber
SELENIUM_SERVER_URL="http://10.0.1.22:4444"
# Application
# HOST_IP="" - defined in Jenkins node
APP_PORT="9092"

View file

@ -0,0 +1,6 @@
# Cucumber
SELENIUM_SERVER_URL="http://10.2.1.4:4444"
# Application
HOST_IP="http://10.2.1.2"
APP_PORT="3000"

View file

@ -0,0 +1,24 @@
const argv = require('minimist')(process.argv);
const common = [
'src/features/home.feature',
'src/features/*.feature',
'--require-module ts-node/register',
'--require src/steps/**/*.ts',
'-f summary',
'-f json:report/report.json',
'-f @qavajs/html-formatter:report/report.html',
].join(' ');
module.exports = (async function () {
if (argv['p'] === 'ci') {
require('dotenv').config({ path: './config/.ci.env' });
} else {
require('dotenv').config({ path: './config/.local.env' });
}
return {
default: common,
ci: common,
};
})();

11
source/selenium/global.d.ts vendored Normal file
View file

@ -0,0 +1,11 @@
declare global {
namespace NodeJS {
interface ProcessEnv {
APP_PORT: string;
HOST_IP: string;
SELENIUM_SERVER_URL: string;
}
}
}
export default global;

View file

@ -0,0 +1,38 @@
{
"name": "$(appName)-selenium",
"version": "0.1.0",
"author": "Roman Jaroš",
"license": "ISC",
"scripts": {
"selenium:smoke": "cucumber-js"
},
"dependencies": {
"typescript": "5.3.3"
},
"devDependencies": {
"@qavajs/html-formatter": "0.15.3",
"@types/chai": "4.3.11",
"@types/selenium-webdriver": "4.1.21",
"@typescript-eslint/eslint-plugin": "6.15.0",
"@typescript-eslint/parser": "6.15.0",
"@cucumber/cucumber": "10.0.1",
"dotenv": "16.3.1",
"eslint": "8.56.0",
"eslint-config-prettier": "9.1.0",
"eslint-plugin-import": "2.29.1",
"eslint-config-next": "14.0.4",
"eslint-plugin-react": "7.33.2",
"eslint-plugin-react-hooks": "4.6.0",
"eslint-plugin-simple-import-sort": "10.0.0",
"eslint-plugin-typescript": "0.14.0",
"eslint-plugin-typescript-sort-keys": "3.1.0",
"chai": "4.3.10",
"cucumber-tsflow": "4.3.1",
"minimist": "1.2.8",
"prettier": "3.1.1",
"selenium-webdriver": "4.16.0",
"ts-loader": "9.5.1",
"ts-node": "10.9.2"
},
"peerDependencies": {}
}

View file

@ -0,0 +1,13 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"moduleResolution": "node",
"emitDecoratorMetadata": false,
"typeRoots": [
"../"
]
},
"ts-node": {
"transpileOnly": true
}
}

View file

@ -0,0 +1,17 @@
import { By } from 'selenium-webdriver';
export class BasePage {
protected url: string;
public title?: string;
public root: By;
public buttons?: Record<string, By>;
public fields?: Record<string, By>;
public forms?: Record<string, By>;
public messages?: Record<string, By>;
public getPageUrl() {
return `${process.env.HOST_IP}:${process.env.APP_PORT}` + this.url;
}
}

View file

@ -0,0 +1,13 @@
import { setDefaultTimeout } from '@cucumber/cucumber';
import { ThenableWebDriver } from 'selenium-webdriver';
import { WebDriver } from './WebDriver';
export class BaseStep {
protected driver: ThenableWebDriver;
public constructor() {
this.driver = new WebDriver().getDriver();
setDefaultTimeout(10 * 60 * 1000);
}
}

View file

@ -0,0 +1,25 @@
import { binding } from 'cucumber-tsflow';
import { Builder, Capabilities, ThenableWebDriver } from 'selenium-webdriver';
const capabilities = Capabilities.chrome();
capabilities.set('chromeOptions', { w3c: false });
@binding()
export class WebDriver {
protected readonly driver: ThenableWebDriver;
private static instance: WebDriver;
public constructor() {
if (WebDriver.instance) {
return WebDriver.instance;
}
this.driver = new Builder().usingServer(process.env.SELENIUM_SERVER_URL).withCapabilities(capabilities).build();
this.driver.manage().setTimeouts({ implicit: 100 });
WebDriver.instance = this;
}
public getDriver() {
return this.driver;
}
}

View file

@ -0,0 +1,5 @@
Feature: Home page
Scenario: Verify home page title
Given i visit home page
Then page title is $(AppName)

View file

@ -0,0 +1,15 @@
import { binding } from 'cucumber-tsflow';
import { By } from 'selenium-webdriver';
import { BasePage } from '../common/BasePage';
@binding()
export class HomePage extends BasePage {
protected url = '/';
// initial div
public root = By.xpath('//body');
// page buttons
public buttons = {};
}

View file

@ -0,0 +1,6 @@
import { By } from 'selenium-webdriver';
export class Navigation {
public root = By.xpath('//div[@class="page-top"]');
public logoutLink = By.xpath(`${this.root.value}//div[contains(text(), "Odhlásit se")]`);
}

View file

@ -0,0 +1,20 @@
import { expect } from 'chai';
import { binding, given } from 'cucumber-tsflow';
import { ThenableWebDriver } from 'selenium-webdriver';
import { WebDriver } from '../../common/WebDriver';
@binding([])
export class PageSteps {
protected driver: ThenableWebDriver;
public constructor() {
this.driver = new WebDriver().getDriver();
}
@given(/page title is (.*)/)
public async thenPageTitleIs(title: string) {
const pageTitle = await this.driver.getTitle();
expect(pageTitle).to.equal(title);
}
}

View file

@ -0,0 +1,24 @@
import { afterAll, binding } from 'cucumber-tsflow';
import { until } from 'selenium-webdriver';
import { WebDriver } from '../../common/WebDriver';
import { Navigation } from '../../pages/components/Navigation';
@binding([])
export class SessionSteps {
@afterAll()
protected async closeBrowser() {
const driver = new WebDriver().getDriver();
await driver.quit();
}
@afterAll()
protected async logout() {
const driver = new WebDriver().getDriver();
const webElements = await driver.findElements(new Navigation().logoutLink);
if (webElements.length > 0) {
await webElements[0]?.click?.();
await driver.wait(until.urlContains(`${process.env.HOST_IP}:${process.env.APP_PORT}`));
}
}
}

View file

@ -0,0 +1,16 @@
import { binding, given } from 'cucumber-tsflow';
import { BaseStep } from '../common/BaseStep';
import { HomePage } from '../pages/HomePage';
@binding([HomePage])
export class HomePageSteps extends BaseStep {
public constructor(public readonly homePage: HomePage) {
super();
}
@given(/i visit home page/)
public async givenIamOnHomePage() {
await this.driver.get(this.homePage.getPageUrl());
}
}