mirror of
https://github.com/argoproj/argo-cd.git
synced 2026-02-20 01:28:45 +01:00
Signed-off-by: Keith Chong <kykchong@redhat.com>
This commit is contained in:
40
ui-test/.env
Normal file
40
ui-test/.env
Normal file
@@ -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
ui-test/.gitignore
vendored
Normal file
6
ui-test/.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
.vscode/
|
||||
.idea/
|
||||
.DS_Store
|
||||
out/
|
||||
node_modules/
|
||||
.env
|
||||
9
ui-test/.prettierrc
Normal file
9
ui-test/.prettierrc
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"bracketSpacing": false,
|
||||
"jsxSingleQuote": true,
|
||||
"printWidth": 180,
|
||||
"singleQuote": true,
|
||||
"tabWidth": 4,
|
||||
"jsxBracketSameLine": true,
|
||||
"quoteProps": "consistent"
|
||||
}
|
||||
32
ui-test/package.json
Normal file
32
ui-test/package.json
Normal file
@@ -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
ui-test/src/Configuration.ts
Normal file
15
ui-test/src/Configuration.ts
Normal file
@@ -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
ui-test/src/Constants.ts
Normal file
4
ui-test/src/Constants.ts
Normal file
@@ -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
ui-test/src/UiTestUtilities.ts
Normal file
134
ui-test/src/UiTestUtilities.ts
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
205
ui-test/src/application-create-panel/application-create-panel.ts
Normal file
205
ui-test/src/application-create-panel/application-create-panel.ts
Normal file
@@ -0,0 +1,205 @@
|
||||
import {By, until, WebDriver} from 'selenium-webdriver';
|
||||
import {Base} from '../base';
|
||||
import UiTestUtilities from '../UiTestUtilities';
|
||||
import * as Const from '../Constants';
|
||||
|
||||
const CREATE_APPLICATION_BUTTON_CREATE: By = By.xpath('.//button[@qe-id="applications-list-button-create"]');
|
||||
const CREATE_APPLICATION_BUTTON_CANCEL: By = By.xpath('.//button[@qe-id="applications-list-button-cancel"]');
|
||||
|
||||
const CREATE_APPLICATION_FIELD_APP_NAME: By = By.xpath('.//input[@qeid="application-create-field-app-name"]');
|
||||
const CREATE_APPLICATION_FIELD_PROJECT: By = By.xpath('.//input[@qe-id="application-create-field-project"]');
|
||||
const CREATE_APPLICATION_FIELD_REPOSITORY_URL: By = By.xpath('.//input[@qe-id="application-create-field-repository-url"]');
|
||||
const CREATE_APPLICATION_FIELD_REPOSITORY_PATH: By = By.xpath('.//input[@qe-id="application-create-field-path"]');
|
||||
|
||||
const CREATE_APPLICATION_DROPDOWN_DESTINATION: By = By.xpath('.//div[@qe-id="application-create-dropdown-destination"]');
|
||||
const CREATE_APPLICATION_DROPDOWN_MENU_URL: By = By.xpath('.//li[@qe-id="application-create-dropdown-destination-URL"]');
|
||||
const CREATE_APPLICATION_DROPDOWN_MENU_NAME: By = By.xpath('.//li[@qe-id="application-create-dropdown-destination-NAME"]');
|
||||
|
||||
export const DESTINATION_MENU_NAME: string = 'NAME';
|
||||
export const DESTINATION_MENU_URL: string = 'URL';
|
||||
|
||||
const CREATE_APPLICATION_FIELD_CLUSTER_NAME: By = By.xpath('.//input[@qe-id="application-create-field-cluster-name"]');
|
||||
const CREATE_APPLICATION_FIELD_CLUSTER_NAMESPACE: By = By.xpath('.//input[@qeid="application-create-field-namespace"]');
|
||||
const CREATE_APPLICATION_FIELD_CLUSTER_URL: By = By.xpath('.//input[@qe-id="application-create-field-cluster-url"]');
|
||||
|
||||
export class ApplicationCreatePanel extends Base {
|
||||
public constructor(driver: WebDriver) {
|
||||
super(driver);
|
||||
}
|
||||
|
||||
public async setAppName(appName: string): Promise<void> {
|
||||
try {
|
||||
const appNameField = await UiTestUtilities.findUiElement(this.driver, CREATE_APPLICATION_FIELD_APP_NAME);
|
||||
await appNameField.sendKeys(appName);
|
||||
} catch (err) {
|
||||
throw new Error(err);
|
||||
}
|
||||
}
|
||||
|
||||
public async setProjectName(projectName: string): Promise<void> {
|
||||
try {
|
||||
const project = await UiTestUtilities.findUiElement(this.driver, CREATE_APPLICATION_FIELD_PROJECT);
|
||||
await project.sendKeys(projectName);
|
||||
} catch (err) {
|
||||
throw new Error(err);
|
||||
}
|
||||
}
|
||||
|
||||
public async setSourceRepoUrl(sourceRepoUrl: string): Promise<void> {
|
||||
try {
|
||||
const reposUrl = await UiTestUtilities.findUiElement(this.driver, CREATE_APPLICATION_FIELD_REPOSITORY_URL);
|
||||
await reposUrl.sendKeys(sourceRepoUrl);
|
||||
} catch (err) {
|
||||
throw new Error(err);
|
||||
}
|
||||
}
|
||||
|
||||
public async setSourceRepoPath(sourceRepoPath: string): Promise<void> {
|
||||
try {
|
||||
const path = await UiTestUtilities.findUiElement(this.driver, CREATE_APPLICATION_FIELD_REPOSITORY_PATH);
|
||||
await path.sendKeys(sourceRepoPath);
|
||||
} catch (err) {
|
||||
throw new Error(err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to select the Destination Cluster URL menu and set the url field with destinationClusterFieldValue
|
||||
*
|
||||
* @param destinationClusterFieldValue
|
||||
*/
|
||||
public async selectDestinationClusterURLMenu(destinationClusterFieldValue: string): Promise<void> {
|
||||
try {
|
||||
const clusterCombo = await UiTestUtilities.findUiElement(this.driver, CREATE_APPLICATION_DROPDOWN_DESTINATION);
|
||||
// click() doesn't work. Use script
|
||||
await UiTestUtilities.click(this.driver, clusterCombo);
|
||||
const urlMenu = await UiTestUtilities.findUiElement(this.driver, CREATE_APPLICATION_DROPDOWN_MENU_URL);
|
||||
await urlMenu.click();
|
||||
if (destinationClusterFieldValue) {
|
||||
await this.setDestinationClusterUrl(destinationClusterFieldValue);
|
||||
}
|
||||
} catch (err) {
|
||||
throw new Error(err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to select the Destination Cluster Name menu and set the namefield with destinationClusterFieldValue
|
||||
*
|
||||
* @param destinationClusterFieldValue
|
||||
*/
|
||||
public async selectDestinationClusterNameMenu(destinationClusterFieldValue: string): Promise<void> {
|
||||
try {
|
||||
const clusterCombo = await UiTestUtilities.findUiElement(this.driver, CREATE_APPLICATION_DROPDOWN_DESTINATION);
|
||||
// click() doesn't work. Use script
|
||||
await UiTestUtilities.click(this.driver, clusterCombo);
|
||||
const nameMenu = await UiTestUtilities.findUiElement(this.driver, CREATE_APPLICATION_DROPDOWN_MENU_NAME);
|
||||
await nameMenu.click();
|
||||
if (destinationClusterFieldValue) {
|
||||
await this.setDestinationClusterName(destinationClusterFieldValue);
|
||||
}
|
||||
} catch (err) {
|
||||
throw new Error(err);
|
||||
}
|
||||
}
|
||||
|
||||
public async setDestinationClusterName(destinationClusterName: string): Promise<void> {
|
||||
try {
|
||||
const clusterName = await UiTestUtilities.findUiElement(this.driver, CREATE_APPLICATION_FIELD_CLUSTER_NAME);
|
||||
await clusterName.sendKeys(destinationClusterName);
|
||||
// await clusterName.sendKeys('\r');
|
||||
} catch (err) {
|
||||
throw new Error(err);
|
||||
}
|
||||
}
|
||||
|
||||
public async setDestinationClusterUrl(destinationClusterUrl: string): Promise<void> {
|
||||
try {
|
||||
const clusterUrl = await UiTestUtilities.findUiElement(this.driver, CREATE_APPLICATION_FIELD_CLUSTER_URL);
|
||||
await clusterUrl.sendKeys(destinationClusterUrl);
|
||||
} catch (err) {
|
||||
throw new Error(err);
|
||||
}
|
||||
}
|
||||
|
||||
public async setDestinationNamespace(destinationNamespace: string): Promise<void> {
|
||||
try {
|
||||
const namespace = await UiTestUtilities.findUiElement(this.driver, CREATE_APPLICATION_FIELD_CLUSTER_NAMESPACE);
|
||||
await namespace.sendKeys(destinationNamespace);
|
||||
} catch (err) {
|
||||
throw new Error(err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Click the Create button to create the app
|
||||
*/
|
||||
public async clickCreateButton(): Promise<void> {
|
||||
try {
|
||||
const createButton = await UiTestUtilities.findUiElement(this.driver, CREATE_APPLICATION_BUTTON_CREATE);
|
||||
await createButton.click();
|
||||
|
||||
// Wait until the Create Application Sliding Panel disappears
|
||||
await this.driver.wait(until.elementIsNotVisible(createButton), Const.TEST_SLIDING_PANEL_TIMEOUT).catch(e => {
|
||||
UiTestUtilities.logError('The Create Application Sliding Panel did not disappear');
|
||||
throw e;
|
||||
});
|
||||
await this.driver.sleep(1000);
|
||||
} catch (err) {
|
||||
throw new Error(err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Click the Cancel Button. Do not create the app.
|
||||
*/
|
||||
public async clickCancelButton(): Promise<void> {
|
||||
try {
|
||||
const cancelButton = await UiTestUtilities.findUiElement(this.driver, CREATE_APPLICATION_BUTTON_CANCEL);
|
||||
await cancelButton.click();
|
||||
|
||||
// Wait until the Create Application Sliding Panel disappears
|
||||
await this.driver.wait(until.elementIsNotVisible(cancelButton), Const.TEST_SLIDING_PANEL_TIMEOUT).catch(e => {
|
||||
UiTestUtilities.logError('The Create Application Sliding Panel did not disappear');
|
||||
throw e;
|
||||
});
|
||||
} catch (err) {
|
||||
throw new Error(err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to create an application given the following inputs to the dialog
|
||||
*
|
||||
* TODO add Sync Policy and Sync Options and setter methods above
|
||||
*
|
||||
* @param appName
|
||||
* @param projectName
|
||||
* @param sourceRepoUrl
|
||||
* @param sourceRepoPath
|
||||
* @param destinationMenu
|
||||
* @param destinationClusterName
|
||||
* @param destinationNamespace
|
||||
*/
|
||||
public async createApplication(
|
||||
appName: string,
|
||||
projectName: string,
|
||||
sourceRepoUrl: string,
|
||||
sourceRepoPath: string,
|
||||
destinationClusterName: string,
|
||||
destinationNamespace: string
|
||||
): Promise<void> {
|
||||
UiTestUtilities.log('About to create application');
|
||||
try {
|
||||
await this.setAppName(appName);
|
||||
await this.setProjectName(projectName);
|
||||
await this.setSourceRepoUrl(sourceRepoUrl);
|
||||
await this.setSourceRepoPath(sourceRepoPath);
|
||||
await this.selectDestinationClusterNameMenu(destinationClusterName);
|
||||
await this.setDestinationNamespace(destinationNamespace);
|
||||
await this.clickCreateButton();
|
||||
} catch (err) {
|
||||
throw new Error(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
199
ui-test/src/applications-list/applications-list.ts
Normal file
199
ui-test/src/applications-list/applications-list.ts
Normal file
@@ -0,0 +1,199 @@
|
||||
import {By, until, WebDriver} from 'selenium-webdriver';
|
||||
import UiTestUtilities from '../UiTestUtilities';
|
||||
import * as Const from '../Constants';
|
||||
import {Base} from '../base';
|
||||
import {ApplicationCreatePanel} from '../application-create-panel/application-create-panel';
|
||||
import {ApplicationsSyncPanel, SYNC_PANEL_SYNCHRONIZE_BUTTON} from '../applications-sync-panel/applications-sync-panel';
|
||||
import {PopupManager} from '../popup/popup-manager';
|
||||
|
||||
const NEW_APP_BUTTON: By = By.xpath('.//button[@qe-id="applications-list-button-new-app"]');
|
||||
// Uncomment to use:
|
||||
// const CREATE_APPLICATION_BUTTON: By = By.xpath('.//button[@qe-id="applications-list-button-create-application"]');
|
||||
|
||||
export class ApplicationsList extends Base {
|
||||
private applicationCreatePanel: ApplicationCreatePanel;
|
||||
private applicationsSyncPanel: ApplicationsSyncPanel;
|
||||
private popupManager: PopupManager;
|
||||
|
||||
public constructor(driver: WebDriver) {
|
||||
super(driver);
|
||||
this.applicationCreatePanel = new ApplicationCreatePanel(driver);
|
||||
this.applicationsSyncPanel = new ApplicationsSyncPanel(driver);
|
||||
this.popupManager = new PopupManager(driver);
|
||||
}
|
||||
|
||||
public async clickTile(appName: string): Promise<void> {
|
||||
try {
|
||||
const tile = await UiTestUtilities.findUiElement(this.driver, this.getApplicationTileLocator(appName));
|
||||
await tile.click();
|
||||
} catch (err) {
|
||||
throw new Error(err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Click the Add New Button
|
||||
*/
|
||||
public async clickNewAppButton(): Promise<ApplicationCreatePanel> {
|
||||
try {
|
||||
const newAppButton = await UiTestUtilities.findUiElement(this.driver, NEW_APP_BUTTON);
|
||||
await newAppButton.click();
|
||||
} catch (err) {
|
||||
throw new Error(err);
|
||||
}
|
||||
return this.applicationCreatePanel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Click the Sync button on the App tile
|
||||
*
|
||||
* @param appName
|
||||
*/
|
||||
public async clickSyncButtonOnApp(appName: string): Promise<ApplicationsSyncPanel> {
|
||||
try {
|
||||
const syncButton = await UiTestUtilities.findUiElement(this.driver, this.getSyncButtonLocatorForApp(appName));
|
||||
await syncButton.click();
|
||||
// Wait until the Synchronize sliding panel appears
|
||||
const synchronizeButton = await this.driver.wait(until.elementLocated(SYNC_PANEL_SYNCHRONIZE_BUTTON), Const.TEST_TIMEOUT);
|
||||
await this.driver.wait(until.elementIsVisible(synchronizeButton), Const.TEST_TIMEOUT);
|
||||
} catch (err) {
|
||||
throw new Error(err);
|
||||
}
|
||||
return this.applicationsSyncPanel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an application via the Delete button on the App tile
|
||||
*
|
||||
* @param appName
|
||||
*/
|
||||
public async clickDeleteButtonOnApp(appName: string): Promise<PopupManager> {
|
||||
try {
|
||||
const deleteButton = await UiTestUtilities.findUiElement(this.driver, this.getDeleteButtonLocatorForApp(appName));
|
||||
await deleteButton.click();
|
||||
} catch (err) {
|
||||
throw new Error(err);
|
||||
}
|
||||
return this.popupManager;
|
||||
}
|
||||
|
||||
public async waitUntilOperationStatusDisappearsOnApp(appName: string) {
|
||||
const opStateElem = await UiTestUtilities.findUiElement(this.driver, this.getApplicationOperationsTitle(appName));
|
||||
await this.driver.wait(async () => {
|
||||
return UiTestUtilities.untilOperationStatusDisappears(opStateElem);
|
||||
}, Const.TEST_TIMEOUT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Click on the Refresh button on the App tile
|
||||
*
|
||||
* @param appName
|
||||
*/
|
||||
public async clickRefreshButtonOnApp(appName: string): Promise<void> {
|
||||
try {
|
||||
const refreshButton = await UiTestUtilities.findUiElement(this.driver, this.getRefreshButtonLocatorForApp(appName));
|
||||
await this.driver.wait(until.elementIsVisible(refreshButton), Const.TEST_TIMEOUT);
|
||||
await refreshButton.click();
|
||||
} catch (err) {
|
||||
throw new Error(err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use with wait. Wait for the health status of the app to change to Healthy
|
||||
*
|
||||
* @param appName
|
||||
*/
|
||||
public async waitForHealthStatusOnApp(appName: string): Promise<void> {
|
||||
try {
|
||||
const healthStatusElement = await UiTestUtilities.findUiElement(this.driver, this.getApplicationHealthTitle(appName));
|
||||
await this.driver.wait(async () => {
|
||||
return UiTestUtilities.untilAttributeIs(healthStatusElement, 'title', 'Healthy');
|
||||
}, Const.TEST_TIMEOUT);
|
||||
} catch (err) {
|
||||
throw new Error(err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use with wait. Wait for the sync status of the app to change to Synced
|
||||
*
|
||||
* @param appName
|
||||
*/
|
||||
public async waitForSyncStatusOnApp(appName: string): Promise<void> {
|
||||
try {
|
||||
const statusElement = await UiTestUtilities.findUiElement(this.driver, this.getApplicationSyncTitle(appName));
|
||||
await this.driver.wait(async () => {
|
||||
return UiTestUtilities.untilAttributeIs(statusElement, 'title', 'Synced');
|
||||
}, Const.TEST_TIMEOUT);
|
||||
} catch (err) {
|
||||
throw new Error(err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that there are no operations associated with the app
|
||||
*
|
||||
* @param appName
|
||||
*/
|
||||
public async checkNoAdditionalOperations(appName: string): Promise<void> {
|
||||
// Check if there are no operations still running
|
||||
UiTestUtilities.log('Checking if there are any additional operations');
|
||||
let opStateElem;
|
||||
let opState;
|
||||
try {
|
||||
opStateElem = await this.driver.wait(until.elementLocated(this.getApplicationOperationsTitle(appName)), Const.TEST_IS_NOT_VISIBLE_TIMEOUT);
|
||||
UiTestUtilities.logError('Unexpected to locate Operation element.');
|
||||
opState = await opStateElem.getAttribute('title');
|
||||
} catch (e) {
|
||||
// ignore since we expect to not have any existing operations
|
||||
}
|
||||
if (opStateElem) {
|
||||
throw new Error('Expecting no other operations. Actual: ' + opState);
|
||||
}
|
||||
}
|
||||
|
||||
// Locators
|
||||
|
||||
// By.css('#app .applications-tiles .applications-list-" + appName + "'');
|
||||
|
||||
private getApplicationTileLocator(appName: string): By {
|
||||
return By.xpath('.//div[contains(@class,"qe-applications-list-"' + appName + ')');
|
||||
}
|
||||
|
||||
private getSyncButtonLocatorForApp(appName: string): By {
|
||||
return By.xpath('.//div[contains(@class, "qe-applications-list-' + appName + '")]//div[@class="row"]//ancestor::a[@qe-id="applications-tiles-button-sync"]');
|
||||
}
|
||||
|
||||
private getDeleteButtonLocatorForApp(appName: string): By {
|
||||
return By.xpath('.//div[contains(@class, "qe-applications-list-' + appName + '")]//div[@class="row"]//ancestor::a[@qe-id="applications-tiles-button-delete"]');
|
||||
}
|
||||
|
||||
private getRefreshButtonLocatorForApp(appName: string): By {
|
||||
return By.xpath('.//div[contains(@class, "qe-applications-list-' + appName + '")]//div[@class="row"]//ancestor::a[@qe-id="applications-tiles-button-refresh"]');
|
||||
}
|
||||
|
||||
private getApplicationHealthTitle(appName: string): By {
|
||||
return By.xpath(
|
||||
'.//div[contains(@class, "qe-applications-list-' +
|
||||
appName +
|
||||
'")]//div[@class="row"]//div[@qe-id="applications-tiles-health-status"]//i[@qe-id="utils-health-status-title"]'
|
||||
);
|
||||
}
|
||||
|
||||
private getApplicationSyncTitle(appName: string): By {
|
||||
return By.xpath(
|
||||
'.//div[contains(@class, "qe-applications-list-' +
|
||||
appName +
|
||||
'")]//div[@class="row"]//div[@qe-id="applications-tiles-health-status"]//i[@qe-id="utils-sync-status-title"]'
|
||||
);
|
||||
}
|
||||
|
||||
private getApplicationOperationsTitle(appName: string): By {
|
||||
return By.xpath(
|
||||
'.//div[contains(@class, "qe-applications-list-' +
|
||||
appName +
|
||||
'")]//div[@class="row"]//div[@qe-id="applications-tiles-health-status"]//i[@qe-id="utils-operations-status-title"]'
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import {By, until, WebDriver} from 'selenium-webdriver';
|
||||
import {Base} from '../base';
|
||||
import * as Const from '../Constants';
|
||||
import UiTestUtilities from '../UiTestUtilities';
|
||||
|
||||
export const SYNC_PANEL_SYNCHRONIZE_BUTTON: By = By.xpath('.//button[@qe-id="application-sync-panel-button-synchronize"]');
|
||||
|
||||
export class ApplicationsSyncPanel extends Base {
|
||||
public constructor(driver: WebDriver) {
|
||||
super(driver);
|
||||
}
|
||||
|
||||
/**
|
||||
* Click the Sync button
|
||||
*/
|
||||
public async clickSyncButton() {
|
||||
try {
|
||||
// Wait until the Synchronize button appears
|
||||
const synchronizeButton = await this.driver.wait(until.elementLocated(SYNC_PANEL_SYNCHRONIZE_BUTTON), Const.TEST_TIMEOUT);
|
||||
await this.driver.wait(until.elementIsVisible(synchronizeButton), Const.TEST_TIMEOUT);
|
||||
|
||||
// Check if the sync button is enabled
|
||||
await this.driver.wait(until.elementIsEnabled(synchronizeButton), Const.TEST_TIMEOUT);
|
||||
await synchronizeButton.click();
|
||||
|
||||
await this.driver.wait(until.elementIsNotVisible(synchronizeButton), Const.TEST_SLIDING_PANEL_TIMEOUT).catch(e => {
|
||||
UiTestUtilities.logError('The Synchronization Sliding Panel did not disappear');
|
||||
throw e;
|
||||
});
|
||||
UiTestUtilities.log('Synchronize sliding panel disappeared');
|
||||
} catch (err) {
|
||||
throw new Error(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
9
ui-test/src/base.ts
Normal file
9
ui-test/src/base.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import {WebDriver} from 'selenium-webdriver';
|
||||
|
||||
export abstract class Base {
|
||||
protected driver: WebDriver;
|
||||
|
||||
public constructor(driver: WebDriver) {
|
||||
this.driver = driver;
|
||||
}
|
||||
}
|
||||
93
ui-test/src/navigation.ts
Normal file
93
ui-test/src/navigation.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
import {By, WebDriver} from 'selenium-webdriver';
|
||||
import {ApplicationsList} from './applications-list/applications-list';
|
||||
import UiTestUtilities from './UiTestUtilities';
|
||||
import {Base} from './base';
|
||||
|
||||
const NAVBAR_APPLICATIONS_BUTTON: By = By.css('#app .nav-bar .argo-icon-application');
|
||||
const NAVBAR_SETTINGS_BUTTON: By = By.css('#app .nav-bar .argo-icon-settings');
|
||||
const NAVBAR_USER_INFO_BUTTON: By = By.css('#app .nav-bar .fa-user-circle');
|
||||
const NAVBAR_DOCS_BUTTON: By = By.css('#app .nav-bar .argo-icon-docs');
|
||||
|
||||
export class Navigation extends Base {
|
||||
private applicationsList: ApplicationsList;
|
||||
|
||||
public constructor(driver: WebDriver) {
|
||||
super(driver);
|
||||
this.applicationsList = new ApplicationsList(this.driver);
|
||||
}
|
||||
|
||||
/**
|
||||
* Click the Applications Nav Bar Button
|
||||
* Return: reference to ApplicationsList page
|
||||
*/
|
||||
public async clickApplicationsNavBarButton(): Promise<ApplicationsList> {
|
||||
try {
|
||||
const navBarButton = await UiTestUtilities.findUiElement(this.driver, NAVBAR_APPLICATIONS_BUTTON);
|
||||
await navBarButton.click();
|
||||
} catch (err) {
|
||||
throw new Error(err);
|
||||
}
|
||||
return this.applicationsList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Click the Settings Nav Bar Button
|
||||
* TODO return settings page
|
||||
*/
|
||||
public async clickSettingsNavBarButton() {
|
||||
try {
|
||||
const navBarButton = await UiTestUtilities.findUiElement(this.driver, NAVBAR_SETTINGS_BUTTON);
|
||||
await navBarButton.click();
|
||||
} catch (err) {
|
||||
throw new Error(err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Click the User Info Nav Bar Button
|
||||
* TODO return User Info page
|
||||
*/
|
||||
public async clickUserInfoNavBarButton() {
|
||||
try {
|
||||
const navBarButton = await UiTestUtilities.findUiElement(this.driver, NAVBAR_USER_INFO_BUTTON);
|
||||
await navBarButton.click();
|
||||
} catch (err) {
|
||||
throw new Error(err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Click the Documentation Nav Bar Button
|
||||
* TODO return docs page
|
||||
*/
|
||||
public async clickDocsNavBarButton() {
|
||||
try {
|
||||
const navBarButton = await UiTestUtilities.findUiElement(this.driver, NAVBAR_DOCS_BUTTON);
|
||||
await navBarButton.click();
|
||||
} catch (err) {
|
||||
throw new Error(err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the WebDriver. Test cases are not recommended to use this. Use Page/Component objects to perform actions
|
||||
*/
|
||||
public getDriver(): WebDriver {
|
||||
return this.driver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call when test case is finished
|
||||
*/
|
||||
public async quit() {
|
||||
await this.driver.quit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sleep for t milliseconds. This is not recommended for use by test cases.
|
||||
* @param t
|
||||
*/
|
||||
public async sleep(t: number) {
|
||||
await this.driver.sleep(t);
|
||||
}
|
||||
}
|
||||
35
ui-test/src/popup/popup-manager.ts
Normal file
35
ui-test/src/popup/popup-manager.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import {By, WebDriver} from 'selenium-webdriver';
|
||||
import {Base} from '../base';
|
||||
import UiTestUtilities from '../UiTestUtilities';
|
||||
|
||||
// Popup Confirmation dialog
|
||||
// Uncomment to use
|
||||
// const POPUP_OK_BUTTON_CSS: By = By.css('#app .popup-container .qe-argo-popup-ok-button');
|
||||
// const POPUP_OK_BUTTON: By = By.xpath('.//button[@qe-id="argo-popup-ok-button"]');
|
||||
// const POPUP_CANCEL_BUTTON: By = By.xpath('.//button[@qe-id="argo-popup-cancel-button"]');
|
||||
|
||||
// Popup Prompt dialog
|
||||
const DELETE_APP_POPUP_FIELD_CONFIRMATION: By = By.xpath('.//input[@qeid="name-field-delete-confirmation"]');
|
||||
const DELETE_APP_PROMPT_POPUP_OK_BUTTON: By = By.xpath('.//button[@qe-id="prompt-popup-ok-button"]');
|
||||
const DELETE_APP_PROMPT_POPUP_CANCEL_BUTTON: By = By.xpath('.//button[@qe-id="prompt-popup-cancel-button"]');
|
||||
|
||||
export class PopupManager extends Base {
|
||||
public constructor(driver: WebDriver) {
|
||||
super(driver);
|
||||
}
|
||||
|
||||
public async setPromptFieldName(appName: string): Promise<void> {
|
||||
const confirmationField = await UiTestUtilities.findUiElement(this.driver, DELETE_APP_POPUP_FIELD_CONFIRMATION);
|
||||
await confirmationField.sendKeys(appName);
|
||||
}
|
||||
|
||||
public async clickPromptOk(): Promise<void> {
|
||||
const okButton = await UiTestUtilities.findUiElement(this.driver, DELETE_APP_PROMPT_POPUP_OK_BUTTON);
|
||||
await okButton.click();
|
||||
}
|
||||
|
||||
public async clickPromptCancel(): Promise<void> {
|
||||
const cancelButton = await UiTestUtilities.findUiElement(this.driver, DELETE_APP_PROMPT_POPUP_CANCEL_BUTTON);
|
||||
await cancelButton.click();
|
||||
}
|
||||
}
|
||||
55
ui-test/src/test001.ts
Normal file
55
ui-test/src/test001.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import Configuration from './Configuration';
|
||||
import UiTestUtilities from './UiTestUtilities';
|
||||
import {trace} from 'console';
|
||||
import {ApplicationsList} from './applications-list/applications-list';
|
||||
import {ApplicationCreatePanel} from './application-create-panel/application-create-panel';
|
||||
import {ApplicationsSyncPanel} from './applications-sync-panel/applications-sync-panel';
|
||||
import {PopupManager} from './popup/popup-manager';
|
||||
|
||||
/**
|
||||
* General test that
|
||||
* - creates an app based on the environment variables (see .env),
|
||||
* - syncs the app
|
||||
* - waits for the healthy and sync'ed states
|
||||
* - deletes the app.
|
||||
*
|
||||
* This can be run multiple times for different apps
|
||||
*
|
||||
*/
|
||||
async function doTest() {
|
||||
const navigation = await UiTestUtilities.init();
|
||||
try {
|
||||
const appsList: ApplicationsList = await navigation.clickApplicationsNavBarButton();
|
||||
const applicationCreatePanel: ApplicationCreatePanel = await appsList.clickNewAppButton();
|
||||
|
||||
UiTestUtilities.log('About to create application');
|
||||
await applicationCreatePanel.setAppName(Configuration.APP_NAME);
|
||||
await applicationCreatePanel.setProjectName(Configuration.APP_PROJECT);
|
||||
await applicationCreatePanel.setSourceRepoUrl(Configuration.GIT_REPO);
|
||||
await applicationCreatePanel.setSourceRepoPath(Configuration.SOURCE_REPO_PATH);
|
||||
await applicationCreatePanel.selectDestinationClusterNameMenu(Configuration.DESTINATION_CLUSTER_NAME);
|
||||
await applicationCreatePanel.setDestinationNamespace(Configuration.DESTINATION_NAMESPACE);
|
||||
await applicationCreatePanel.clickCreateButton();
|
||||
|
||||
const appsSyncPanel: ApplicationsSyncPanel = await appsList.clickSyncButtonOnApp(Configuration.APP_NAME);
|
||||
await appsSyncPanel.clickSyncButton();
|
||||
|
||||
await appsList.waitForHealthStatusOnApp(Configuration.APP_NAME);
|
||||
await appsList.waitForSyncStatusOnApp(Configuration.APP_NAME);
|
||||
await appsList.checkNoAdditionalOperations(Configuration.APP_NAME);
|
||||
|
||||
const popupManager: PopupManager = await appsList.clickDeleteButtonOnApp(Configuration.APP_NAME);
|
||||
await popupManager.setPromptFieldName(Configuration.APP_NAME);
|
||||
await popupManager.clickPromptOk();
|
||||
// After deleting, wait until the delete operation finishes
|
||||
await appsList.waitUntilOperationStatusDisappearsOnApp(Configuration.APP_NAME);
|
||||
|
||||
await UiTestUtilities.log('Test passed');
|
||||
} catch (e) {
|
||||
trace('Test failed ', e);
|
||||
} finally {
|
||||
await navigation.quit();
|
||||
}
|
||||
}
|
||||
|
||||
doTest();
|
||||
27
ui-test/src/test002.ts
Normal file
27
ui-test/src/test002.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import UiTestUtilities from './UiTestUtilities';
|
||||
import {trace} from 'console';
|
||||
import {ApplicationsList} from './applications-list/applications-list';
|
||||
import {ApplicationCreatePanel} from './application-create-panel/application-create-panel';
|
||||
|
||||
/**
|
||||
* Test to demo how to visit each page via the navigation bar on the left.
|
||||
*
|
||||
*/
|
||||
async function doTest() {
|
||||
const navigation = await UiTestUtilities.init();
|
||||
try {
|
||||
await navigation.clickDocsNavBarButton();
|
||||
await navigation.clickUserInfoNavBarButton();
|
||||
await navigation.clickSettingsNavBarButton();
|
||||
const appsList: ApplicationsList = await navigation.clickApplicationsNavBarButton();
|
||||
const applicationCreatePanel: ApplicationCreatePanel = await appsList.clickNewAppButton();
|
||||
await applicationCreatePanel.clickCancelButton();
|
||||
await UiTestUtilities.log('Test passed');
|
||||
} catch (e) {
|
||||
trace('Test failed ', e);
|
||||
} finally {
|
||||
await navigation.quit();
|
||||
}
|
||||
}
|
||||
|
||||
doTest();
|
||||
74
ui-test/tsconfig.json
Normal file
74
ui-test/tsconfig.json
Normal file
@@ -0,0 +1,74 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
/* Visit https://aka.ms/tsconfig.json to read more about this file */
|
||||
|
||||
/* Basic Options */
|
||||
// "incremental": true, /* Enable incremental compilation */
|
||||
"target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
|
||||
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
|
||||
"lib": ["es2017"], /* Specify library files to be included in the compilation. */
|
||||
"allowJs": true, /* Allow javascript files to be compiled. */
|
||||
// "checkJs": true, /* Report errors in .js files. */
|
||||
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
|
||||
// "declaration": true, /* Generates corresponding '.d.ts' file. */
|
||||
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
|
||||
"sourceMap": true, /* Generates corresponding '.map' file. */
|
||||
// "outFile": "./", /* Concatenate and emit output to single file. */
|
||||
"outDir": "out", /* Redirect output structure to the directory. */
|
||||
"rootDir": "src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
|
||||
// "composite": true, /* Enable project compilation */
|
||||
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
|
||||
// "removeComments": true, /* Do not emit comments to output. */
|
||||
// "noEmit": true, /* Do not emit outputs. */
|
||||
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
|
||||
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
|
||||
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
|
||||
|
||||
/* Strict Type-Checking Options */
|
||||
"strict": true, /* Enable all strict type-checking options. */
|
||||
"noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
|
||||
// "strictNullChecks": true, /* Enable strict null checks. */
|
||||
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
|
||||
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
|
||||
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
|
||||
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
|
||||
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
|
||||
|
||||
/* Additional Checks */
|
||||
"noUnusedLocals": true, /* Report errors on unused locals. */
|
||||
"noUnusedParameters": true, /* Report errors on unused parameters. */
|
||||
"noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
|
||||
|
||||
/* Module Resolution Options */
|
||||
"moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
|
||||
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
|
||||
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
|
||||
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
||||
// "typeRoots": [], /* List of folders to include type definitions from. */
|
||||
// "types": [], /* Type declaration files to be included in compilation. */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
|
||||
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
|
||||
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
|
||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||
|
||||
/* Source Map Options */
|
||||
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
|
||||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
|
||||
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
|
||||
|
||||
/* Experimental Options */
|
||||
"experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
|
||||
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
|
||||
|
||||
/* Advanced Options */
|
||||
// "resolveJsonModule": true, /* Include modules imported with '.json' extension */
|
||||
"skipLibCheck": true, /* Skip type checking of declaration files. */
|
||||
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"out"
|
||||
]
|
||||
}
|
||||
17
ui-test/tslint.json
Normal file
17
ui-test/tslint.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"extends": [
|
||||
"tslint:recommended", "tslint-plugin-prettier", "tslint-config-prettier"
|
||||
],
|
||||
"jsRules": {},
|
||||
"rules": {
|
||||
"prettier": true,
|
||||
"quotemark": [true, "single"],
|
||||
"no-var-requires": false,
|
||||
"interface-name": false,
|
||||
"object-literal-sort-keys": false,
|
||||
"max-line-length": [true, 200],
|
||||
"array-type": false,
|
||||
"max-classes-per-file": false
|
||||
},
|
||||
"rulesDirectory": []
|
||||
}
|
||||
1529
ui-test/yarn.lock
Normal file
1529
ui-test/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user