Skip to content

Commit

Permalink
test: Add initial automated UI smoke tests (argoproj#4393) (argoproj#…
Browse files Browse the repository at this point in the history
…4694)

Signed-off-by: Keith Chong <[email protected]>
  • Loading branch information
keithchong authored Jul 29, 2021
1 parent c7738a0 commit 4a69cce
Show file tree
Hide file tree
Showing 18 changed files with 2,518 additions and 0 deletions.
40 changes: 40 additions & 0 deletions ui-test/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#
# Currently defined test environment variables. Uncomment and/or change as desired.
#
############################
# Test specific variables
############################
#
# Timeout to wait for an element to appear. The default is 60 sec.
# TEST_TIMEOUT=60000
#
# Run the tests in headless mode if true, non-headless mode if false
IS_HEADLESS=true
#
# Turn on/off tracing to the console. The default is true.
# ENABLE_CONSOLE_LOG=true
#
############################
# ArgoCD specific variables
############################
#
# URL of the ArgoCD UI to test against
ARGOCD_URL=http://localhost:4000
#
# Git repository where applications reside
GIT_REPO=https://github.com/argoproj/argocd-example-apps
#
# The name to give the app in ArgoCD
APP_NAME=myapp
#
# The project name
APP_PROJECT=default
#
# The source path of the application in the repo
SOURCE_REPO_PATH=helm-guestbook
#
# Destination cluster name
DESTINATION_CLUSTER_NAME=in-cluster
#
# Destination namespace
DESTINATION_NAMESPACE=default
6 changes: 6 additions & 0 deletions ui-test/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.vscode/
.idea/
.DS_Store
out/
node_modules/
.env
9 changes: 9 additions & 0 deletions ui-test/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"bracketSpacing": false,
"jsxSingleQuote": true,
"printWidth": 180,
"singleQuote": true,
"tabWidth": 4,
"jsxBracketSameLine": true,
"quoteProps": "consistent"
}
32 changes: 32 additions & 0 deletions ui-test/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"name": "ui-test",
"version": "1.0.0",
"description": "UI Testing",
"main": "argocd-ui-test",
"scripts": {
"compile": "npx tsc",
"test": "node out/test001.js",
"pretest": "cp .env out/.env",
"lint": "tslint -p ."
},
"author": "Keith Chong",
"license": "Apache-2.0",
"dependencies": {
"@types/selenium-webdriver": "^4.0.9",
"assert": "^2.0.0",
"chromedriver": "^86.0.0",
"selenium-webdriver": "^4.0.0-alpha.7"
},
"devDependencies": {
"@types/mocha": "^8.0.3",
"@types/node": "^14.14.2",
"dotenv": "^8.2.0",
"mocha": "^8.2.0",
"prettier": "^1.18.2",
"tslint": "^6.1.3",
"tslint-config-prettier": "^1.18.0",
"tslint-plugin-prettier": "^2.0.1",
"typescript": "^4.0.3",
"yarn": "^1.22.10"
}
}
15 changes: 15 additions & 0 deletions ui-test/src/Configuration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
require('dotenv').config({path: __dirname + '/.env'});

export default class Configuration {
// Test specific
public static readonly ENABLE_CONSOLE_LOG: string | undefined = process.env.ENABLE_CONSOLE_LOG;
public static readonly TEST_TIMEOUT: string | undefined = process.env.TEST_TIMEOUT;
// ArgoCD UI specific. These are for single application-based tests, so one can quickly create an app based on the environment variables
public static readonly ARGOCD_URL: string = process.env.ARGOCD_URL ? process.env.ARGOCD_URL : '';
public static readonly APP_NAME: string = process.env.APP_NAME ? process.env.APP_NAME : '';
public static readonly APP_PROJECT: string = process.env.APP_PROJECT ? process.env.APP_PROJECT : '';
public static readonly GIT_REPO: string = process.env.GIT_REPO ? process.env.GIT_REPO : '';
public static readonly SOURCE_REPO_PATH: string = process.env.SOURCE_REPO_PATH ? process.env.SOURCE_REPO_PATH : '';
public static readonly DESTINATION_CLUSTER_NAME: string = process.env.DESTINATION_CLUSTER_NAME ? process.env.DESTINATION_CLUSTER_NAME : '';
public static readonly DESTINATION_NAMESPACE: string = process.env.DESTINATION_NAMESPACE ? process.env.DESTINATION_NAMESPACE : '';
}
4 changes: 4 additions & 0 deletions ui-test/src/Constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const TEST_TIMEOUT: number = 60000;
export const TEST_SLIDING_PANEL_TIMEOUT: number = 5000;
export const TEST_IS_NOT_VISIBLE_TIMEOUT: number = 5000;
export const ENABLE_CONSOLE_LOG: boolean = true;
134 changes: 134 additions & 0 deletions ui-test/src/UiTestUtilities.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import Configuration from './Configuration';
import {Builder, By, until, WebDriver, WebElement} from 'selenium-webdriver';
import chrome from 'selenium-webdriver/chrome';
import * as Const from './Constants';
import {Navigation} from './navigation';

export default class UiTestUtilities {
/**
* Log a message to the console.
* @param message
*/
public static async log(message: string): Promise<void> {
let doLog = Const.ENABLE_CONSOLE_LOG;
// Config override
if (Configuration.ENABLE_CONSOLE_LOG) {
if (Configuration.ENABLE_CONSOLE_LOG === 'false') {
doLog = false;
} else {
doLog = true;
}
}
if (doLog) {
// tslint:disable-next-line:no-console
console.log(message);
}
}

public static async logError(message: string): Promise<void> {
let doLog = Const.ENABLE_CONSOLE_LOG;
// Config override
if (Configuration.ENABLE_CONSOLE_LOG) {
if (Configuration.ENABLE_CONSOLE_LOG === 'false') {
doLog = false;
} else {
doLog = true;
}
}
if (doLog) {
// tslint:disable-next-line:no-console
console.error(message);
}
}

/**
* Set up the WebDriver. Initial steps for all tests. Returns the instance of Navigation with the WebDriver.
* From there, navigate the UI. Test cases do no need to reference the instance of WebDriver since Component/Page-specific
* API methods should be called instead.
*
*/
public static async init(): Promise<Navigation> {
const options = new chrome.Options();
if (process.env.IS_HEADLESS) {
options.addArguments('headless');
}
options.addArguments('window-size=1400x1200');
const driver = await new Builder()
.forBrowser('chrome')
.setChromeOptions(options)
.build();

UiTestUtilities.log('Environment variables are:');
UiTestUtilities.log(require('dotenv').config({path: __dirname + '/../.env'}));

// Navigate to the ArgoCD URL
await driver.get(Configuration.ARGOCD_URL);

return new Navigation(driver);
}

/**
* Locate the UI Element for the given locator, and wait until it is visible
*
* @param driver
* @param locator
*/
public static async findUiElement(driver: WebDriver, locator: By): Promise<WebElement> {
try {
let timeout = Const.TEST_TIMEOUT;
if (Configuration.TEST_TIMEOUT) {
timeout = parseInt(Configuration.TEST_TIMEOUT, 10);
}
const element = await driver.wait(until.elementLocated(locator), timeout);
await driver.wait(until.elementIsVisible(element), timeout);
return element;
} catch (err) {
throw err;
}
}

/**
* Similar to until.methods and used in driver.wait, this will wait until
* the expected attribute is the same as the actual attribute on the element
*
* @param attr
* @param attrValue
*/
public static async untilAttributeIs(element: WebElement, attr: string, attrValue: string): Promise<boolean> {
const actual = await element.getAttribute(attr);
UiTestUtilities.log('Actual = ' + actual + ', expected = ' + attrValue + ', ' + (actual === attrValue));
return actual === attrValue;
}

/**
* Similar to until.methods and used in driver.wait, this function will wait until
* the element (eg. operation state) title attribute no longer is present
*
* @param element
*/
public static async untilOperationStatusDisappears(element: WebElement): Promise<boolean> {
try {
const opState = await element.getAttribute('title');
UiTestUtilities.log('Operation State = ' + opState);
return false;
} catch (err) {
UiTestUtilities.log('Status disappeared');
return true;
}
}

/**
* For clicking on elements if WebElement.click() doesn't work
*
* @param driver
* @param element
*/
public static async click(driver: WebDriver, element: WebElement): Promise<void> {
try {
// Execute synchronous script
await driver.executeScript('arguments[0].click();', element);
} catch (e) {
throw e;
}
}
}
Loading

0 comments on commit 4a69cce

Please sign in to comment.