This is an automated email from the ASF dual-hosted git repository.
zrhoffman pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git
The following commit(s) were added to refs/heads/master by this push:
new 30c0feb TP tests alert logging (#6120)
30c0feb is described below
commit 30c0feb6904de74517dd927df4c721f342105e57
Author: ocket8888 <[email protected]>
AuthorDate: Fri Aug 20 11:15:17 2021 -0600
TP tests alert logging (#6120)
* Move `hasProperty` to its own module and provide type-specific property
checking
* Add a data model for global testing configuration that can define log
levels
* Make API take a configuration parameter and export an instance globally
* Add common logging of Alerts to all axios requests in the API class
* Update API usage to use new global export
* Fix implicit 'any' type detection in old TS versions
* Put TO logs in artifacts, clear up console output
This also fixes assumptions that asynchronous background jobs will have
reached a certain point by the time the foreground reaches a certain
point, and adds debug output to the TO logs. Plus uses `npm` scripts
instead of raw commands.
* Move Riak logs into artifacts, out of console
* Fix not restoring directory state after starting TP, moving open file
descriptors
* upload artifacts on all failures, not just test failures
* Fix docker logs leaking stderr
* log full URL
* Wait for TO to start before continuing
* fix passing API requests through TP instead of straight to TO
* fix extra path separator in url building
* Fix incorrect info log, remove commented-out code
* Simplify checking property existence and type with new hasProperty call
signatures
* Remove redundant option
* change debug logs to stdout
---
.github/actions/tp-integration-tests/cdn.json | 10 +-
.github/actions/tp-integration-tests/entrypoint.sh | 90 ++++-----
traffic_portal/test/integration/CommonUtils/API.ts | 211 ++++++++++++++-------
.../test/integration/CommonUtils/index.ts | 21 ++
.../test/integration/CommonUtils/utils.ts | 133 +++++++++++++
traffic_portal/test/integration/config.model.ts | 165 ++++++++++++++++
traffic_portal/test/integration/config.ts | 21 +-
traffic_portal/test/integration/specs/ASNs.spec.ts | 3 +-
.../test/integration/specs/Coordinates.spec.ts | 3 +-
.../integration/specs/DeliveryServices.spec.ts | 5 +-
.../test/integration/specs/Divisions.spec.ts | 4 +-
traffic_portal/test/integration/specs/Jobs.spec.ts | 3 +-
.../test/integration/specs/Origins.spec.ts | 3 +-
.../test/integration/specs/Parameters.spec.ts | 5 +-
.../test/integration/specs/PhysLocations.spec.ts | 3 +-
.../test/integration/specs/Profiles.spec.ts | 3 +-
.../test/integration/specs/Regions.spec.ts | 3 +-
.../specs/ServerServerCapabilities.spec.ts | 3 +-
.../test/integration/specs/Servers.spec.ts | 3 +-
.../integration/specs/ServiceCategories.spec.ts | 3 +-
.../test/integration/specs/Statuses.spec.ts | 3 +-
.../test/integration/specs/Topologies.spec.ts | 6 +-
.../test/integration/specs/Types.spec.ts | 3 +-
23 files changed, 554 insertions(+), 153 deletions(-)
diff --git a/.github/actions/tp-integration-tests/cdn.json
b/.github/actions/tp-integration-tests/cdn.json
index e2ac195..6bd2b68 100644
--- a/.github/actions/tp-integration-tests/cdn.json
+++ b/.github/actions/tp-integration-tests/cdn.json
@@ -14,11 +14,11 @@
"traffic_ops_golang": {
"insecure": true,
"port": "6443",
- "log_location_error": "error.log",
- "log_location_warning": "warning.log",
- "log_location_info": "info.log",
- "log_location_debug": null,
- "log_location_event": "event.log",
+ "log_location_error": "stderr",
+ "log_location_warning": "stderr",
+ "log_location_info": "stdout",
+ "log_location_debug": "stdout",
+ "log_location_event": "stdout",
"max_db_connections": 20,
"db_conn_max_lifetime_seconds": 60,
"db_query_timeout_seconds": 20,
diff --git a/.github/actions/tp-integration-tests/entrypoint.sh
b/.github/actions/tp-integration-tests/entrypoint.sh
index afd3b8f..6e6ca2d 100755
--- a/.github/actions/tp-integration-tests/entrypoint.sh
+++ b/.github/actions/tp-integration-tests/entrypoint.sh
@@ -15,7 +15,35 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-trap 'echo "Error on line ${LINENO} of ${0}"; exit 1' ERR
+
+onFail() {
+ echo "Error on line ${1} of ${2}" >&2;
+ if ! [[ -d Reports ]]; then
+ mkdir Reports;
+ fi
+ if [[ -f tv.log ]]; then
+ cp tv.log Reports/traffic_vault.docker.log;
+ fi
+ docker logs "$trafficvault" > Reports/traffic_vault.log 2>&1;
+ if [[ -f tp.log ]]; then
+ mv tp.log Reports/forever.log
+ fi
+ if [[ -f access.log ]]; then
+ mv access.log Reports/tp-access.log
+ fi
+ if [[ -f out.log ]]; then
+ mv out.log Reports/node.log
+ fi
+ docker logs $CHROMIUM_CONTAINER > Reports/chromium.log 2>&1;
+ docker logs $HUB_CONTAINER > Reports/hub.log 2>&1;
+ if [[ -f "${REPO_DIR}/traffic_ops/traffic_ops_golang" ]]; then
+ cp "${REPO_DIR}/traffic_ops/traffic_ops_golang" Reports/to.log;
+ fi
+ echo "Detailed logs produced info Reports artifact"
+ exit 1
+}
+
+trap 'onFail "${LINENO}" "${0}"' ERR
set -o errexit -o nounset -o pipefail
hub_fqdn="http://localhost:4444/wd/hub/status"
@@ -23,7 +51,7 @@ to_fqdn="https://localhost:6443"
tp_fqdn="https://172.18.0.1:8443"
if ! curl -Lvsk "${hub_fqdn}" >/dev/null 2>&1; then
- echo "Selenium not started on ${hub_fqdn}"
+ echo "Selenium not started on ${hub_fqdn}" >&2;
exit 1
fi
@@ -81,19 +109,6 @@ QUERY
sudo useradd trafops
-gray_bg="$(printf '%s%s' $'\x1B' '[100m')";
-red_bg="$(printf '%s%s' $'\x1B' '[41m')";
-yellow_bg="$(printf '%s%s' $'\x1B' '[43m')";
-black_fg="$(printf '%s%s' $'\x1B' '[30m')";
-color_and_prefix() {
- color="$1";
- shift;
- prefix="$1";
- normal_bg="$(printf '%s%s' $'\x1B' '[49m')";
- normal_fg="$(printf '%s%s' $'\x1B' '[39m')";
- sed "s/^/${color}${black_fg}${prefix}: /" | sed
"s/$/${normal_bg}${normal_fg}/";
-}
-
ciab_dir="${GITHUB_WORKSPACE}/infrastructure/cdn-in-a-box";
trafficvault=trafficvault;
start_traffic_vault() {
@@ -127,9 +142,9 @@ start_traffic_vault() {
--publish=8087:8087 \
--rm \
"$trafficvault" \
- /usr/lib/riak/riak-cluster.sh;
+ /usr/lib/riak/riak-cluster.sh
}
-start_traffic_vault &
+start_traffic_vault >tv.log 2>&1 &
sudo apt-get install -y --no-install-recommends gettext \
ruby ruby-dev libc-dev curl \
@@ -154,7 +169,7 @@ if [[ ! -e "$REPO_DIR" ]]; then
fi
to_build() {
- cd "${REPO_DIR}/traffic_ops/traffic_ops_golang"
+ pushd "${REPO_DIR}/traffic_ops/traffic_ops_golang"
if [[ ! -d "${GITHUB_WORKSPACE}/vendor/golang.org" ]]; then
go mod vendor
fi
@@ -167,56 +182,42 @@ to_build() {
export $(<"${ciab_dir}/variables.env" sed '/^#/d') # defines
TV_ADMIN_USER/PASSWORD
envsubst <"${resources}/riak.json" >riak.conf
- truncate --size=0 warning.log error.log event.log info.log
+ truncate -s0 out.log
- ./traffic_ops_golang --cfg ./cdn.conf --dbcfg ./database.conf -riakcfg
riak.conf &
- tail -f warning.log 2>&1 | color_and_prefix "${yellow_bg}" 'Traffic Ops
WARN' &
- tail -f error.log 2>&1 | color_and_prefix "${red_bg}" 'Traffic Ops ERR' &
- tail -f event.log 2>&1 | color_and_prefix "${gray_bg}" 'Traffic Ops EVT' &
+ ./traffic_ops_golang --cfg ./cdn.conf --dbcfg ./database.conf -riakcfg
riak.conf >out.log 2>&1 &
+ popd
}
tp_build() {
- cd "${REPO_DIR}/traffic_portal"
+ pushd "${REPO_DIR}/traffic_portal"
npm ci
bower install
grunt dist
cp "${resources}/config.js" ./conf/
touch tp.log access.log out.log err.log
- sudo forever --minUptime 5000 --spinSleepTime 2000 -f -o out.log start
server.js &
- tail -f err.log 2>&1 | color_and_prefix "${red_bg}" "Node Err" &
+ sudo forever --minUptime 5000 --spinSleepTime 2000 -f start server.js
>out.log 2>&1 &
+ popd
}
-(to_build) &
-(tp_build) &
-
-onFail() {
- docker logs "$trafficvault" > Reports/traffic_vault.log
- mv tp.log Reports/forever.log
- mv access.log Reports/tp-access.log
- mv out.log Reports/node.log
- docker logs $CHROMIUM_CONTAINER > Reports/chromium.log
- docker logs $HUB_CONTAINER > Reports/hub.log
- echo "Detailed logs produced info Reports artifact"
- exit 1
-}
+to_build
+tp_build
cd "${REPO_DIR}/traffic_portal/test/integration"
npm ci
-PATH=$(pwd)/node_modules/.bin/:$PATH
-webdriver-manager update --gecko false --versions.chrome
"LATEST_RELEASE_$CHROMIUM_VER"
+./node_modules/.bin/webdriver-manager update --gecko false --versions.chrome
"LATEST_RELEASE_$CHROMIUM_VER"
jq " .capabilities.chromeOptions.args = [
\"--headless\",
\"--no-sandbox\",
\"--disable-gpu\",
\"--ignore-certificate-errors\"
- ] | .params.apiUrl = \"${tp_fqdn}/api/4.0\" | .params.baseUrl =\"${tp_fqdn}\"
+ ] | .params.apiUrl = \"${to_fqdn}/api/4.0\" | .params.baseUrl =\"${tp_fqdn}\"
| .capabilities[\"goog:chromeOptions\"].w3c = false |
.capabilities.chromeOptions.w3c = false" \
config.json > config.json.tmp && mv config.json.tmp config.json
-tsc
+npm run build
# Wait for tp/to build
timeout 5m bash <<TMOUT
@@ -226,5 +227,4 @@ timeout 5m bash <<TMOUT
done
TMOUT
-trap - ERR
-protractor ./GeneratedCode/config.js --params.baseUrl="${tp_fqdn}"
--params.apiUrl="${to_fqdn}/api/4.0" || onFail
+npm test -- --params.baseUrl="${tp_fqdn}" --params.apiUrl="${to_fqdn}/api/4.0"
diff --git a/traffic_portal/test/integration/CommonUtils/API.ts
b/traffic_portal/test/integration/CommonUtils/API.ts
index 9b18768..2a626a3 100644
--- a/traffic_portal/test/integration/CommonUtils/API.ts
+++ b/traffic_portal/test/integration/CommonUtils/API.ts
@@ -21,10 +21,12 @@
import { Agent } from "https";
import axios from 'axios';
-import type {AxiosResponse} from "axios";
+import type {AxiosResponse, AxiosError} from "axios";
import randomIpv6 from "random-ipv6";
-import { config, randomize } from '../config';
+import { hasProperty } from "./utils";
+import { randomize } from '../config';
+import { AlertLevel, isAlert, logAlert, TestingConfig } from "../config.model";
interface GetRequest {
queryKey: string;
@@ -50,33 +52,61 @@ export interface APIData {
}
/**
- * hasProperty checks, generically, whether some variable passed as `o` has the
- * property `k`.
+ * Checks if an object is an AxiosError, usually useful in `try`/`catch` blocks
+ * around axios calls.
*
- * @example
- * hasProperty({}, "id"); // returns false
- * hasProperty({id: 8}); // returns true
- * hasProperty({id: undefined}); // returns true
- *
- * @param o The object to check.
- * @param k The key for which to check in the object.
- * @returns Whether or not `o` has the property `k`.
- * @throws {Error} when the type check fails.
+ * @param e The object to check.
+ * @returns Whether or not `e` is an AxiosError.
*/
- export function hasProperty<T extends object, K extends PropertyKey, S>(o: T,
k: K): o is T & Record<K, S | unknown> {
- return Object.prototype.hasOwnProperty.call(o, k);
+function isAxiosError(e: unknown): e is AxiosError {
+ if (typeof(e) !== "object" || e === null) {
+ return false;
+ }
+ if (!hasProperty(e, "isAxiosError", "boolean")) {
+ return false;
+ }
+ return e.isAxiosError;
}
export class API {
private cookie = "";
- private readonly config = config;
- constructor() {
+ /**
+ * This controls the alert levels that get logged - levels not in this set
+ * are not logged
+ */
+ private readonly alertLevels = new Set<AlertLevel>(["warning", "error",
"info"]);
+ /**
+ * Stores login information for the admin-level user.
+ */
+ private readonly loginInfo: {
+ password: string;
+ username: string;
+ };
+ /**
+ * The URL base used for the Traffic Ops API.
+ *
+ * Trailing `/` is guaranteed.
+ *
+ * @example
+ * "https://localhost:6443/api/4.0/"
+ */
+ private readonly apiURL: string;
+
+ /**
+ * @param cfg The testing configuration.
+ */
+ constructor(cfg: TestingConfig) {
axios.defaults.headers.common['Accept'] = 'application/json'
axios.defaults.headers.common['Authorization'] = 'No-Auth'
axios.defaults.headers.common['Content-Type'] = 'application/json'
axios.defaults.httpsAgent = new Agent({ rejectUnauthorized: false })
+ if (cfg.alertLevels) {
+ this.alertLevels = new Set(cfg.alertLevels);
+ }
+ this.loginInfo = cfg.login;
+ this.apiURL = cfg.apiUrl.endsWith("/") ? cfg.apiUrl : `${cfg.apiUrl}/`;
}
/**
@@ -86,18 +116,90 @@ export class API {
* @throws {Error} when login fails, or when Traffic Ops doesn't return a
cookie.
*/
public async Login(): Promise<AxiosResponse<unknown>> {
- const response = await axios({
- method: 'post',
- url: this.config.params.apiUrl + '/user/login',
- data: {
- u: this.config.params.login.username,
- p: this.config.params.login.password
- }
- });
- this.cookie = await response.headers["set-cookie"][0];
+ const data = {
+ p: this.loginInfo.password,
+ u: this.loginInfo.username,
+ }
+ const response = await this.getResponse("post", "/user/login", data);
+ const h = response.headers as object;
+ if (!hasProperty(h, "set-cookie", "Array") || h["set-cookie"].length <
1) {
+ throw new Error("Traffic Ops response did not set a cookie");
+ }
+ const cookie = await h["set-cookie"][0];
+ if (typeof(cookie) !== "string") {
+ throw new Error(`non-string cookie: ${cookie}`);
+ }
+ this.cookie = cookie;
return response
}
+ /**
+ * Retrieves a response from the API.
+ *
+ * Alerts will be logged if they are found - even if an error occurs and is
+ * thrown.
+ *
+ * @param method The request method to use.
+ * @param path The path to request, relative to the configured TO API URL.
+ * @returns The server's response.
+ * @throws {unknown} when the request fails for any reason. If an error
+ * response was returned from the API, it was logged, so there's no need to
+ * dig into the properties of these errors, really.
+ */
+ private async getResponse(method: "get" | "delete", path: string):
Promise<AxiosResponse>;
+ /**
+ * Retrieves a response from the API.
+ *
+ * Alerts will be logged if they are found - even if an error occurs and is
+ * thrown.
+ *
+ * @param method The request method to use.
+ * @param path The path to request, relative to the configured TO API URL.
+ * @param data Data to send in the body of the POST request.
+ * @returns The server's response.
+ * @throws {unknown} when the request fails for any reason. If an error
+ * response was returned from the API, it was logged, so there's no need to
+ * dig into the properties of these errors, really.
+ */
+ private async getResponse(method: "post", path: string, data: unknown):
Promise<AxiosResponse>;
+ private async getResponse(method: "post" | "get" | "delete", path: string,
data?: unknown): Promise<AxiosResponse> {
+ if (method === "post" && data === undefined) {
+ throw new TypeError("request body must be given for POST
requests");
+ }
+
+ const url = `${this.apiURL}${path.replace(/^\/+/g, "")}`;
+ const conf = {
+ method,
+ url,
+ headers: { Cookie: this.cookie },
+ data
+ }
+
+ let throwable;
+ let resp: AxiosResponse<unknown>;
+ try {
+ resp = await axios(conf);
+ } catch(e) {
+ if (!isAxiosError(e) || !e.response) {
+ console.debug("non-axios error or axios error with no response
thrown");
+ throw e;
+ }
+ resp = e.response;
+ throwable = e;
+ }
+ if (typeof(resp.data) === "object" && resp.data !== null &&
hasProperty(resp.data, "alerts", "Array")) {
+ for (const a of resp.data.alerts) {
+ if (isAlert(a) && this.alertLevels.has(a.level)) {
+ logAlert(a, `${method.toUpperCase()} ${url}
(${resp.status} ${resp.statusText}):`);
+ }
+ }
+ }
+ if (throwable) {
+ throw throwable;
+ }
+ return resp;
+ }
+
public async SendRequest<T extends IDData>(route: string, method: string,
data: T): Promise<void> {
let response
this.Randomize(data)
@@ -117,19 +219,10 @@ export class API {
switch (method) {
case "post":
- response = await axios({
- method: method,
- url: this.config.params.apiUrl + route,
- headers: { Cookie: this.cookie},
- data: data
- });
+ response = await this.getResponse("post", route, data);
break;
case "get":
- response = await axios({
- method: method,
- url: this.config.params.apiUrl + route,
- headers: { Cookie: this.cookie},
- });
+ response = await this.getResponse("get", route);
break;
case "delete":
if (!data.route) {
@@ -147,11 +240,7 @@ export class API {
if((data.route).includes('/service_categories/')){
data.route = data.route + randomize
}
- response = await axios({
- method: method,
- url: this.config.params.apiUrl + data.route,
- headers: { Cookie: this.cookie},
- });
+ response = await this.getResponse("delete", data.route);
break;
default:
throw new Error(`unrecognized request method: '${method}'`);
@@ -171,11 +260,7 @@ export class API {
}
for (const request of data.getRequest) {
const query =
`?${encodeURIComponent(request.queryKey)}=${encodeURIComponent(request.queryValue)}${randomize}`;
- const response = await axios({
- method: 'get',
- url: this.config.params.apiUrl + request.route + query,
- headers: { Cookie: this.cookie},
- });
+ const response = await this.getResponse("get", request.route +
query)
if (response.status == 200) {
if(request.hasOwnProperty('isArray')){
@@ -193,7 +278,7 @@ export class API {
return null
}
- public Randomize(data: object): void {
+ public Randomize(data: object): void {
if (hasProperty(data, "fullName")) {
if (hasProperty(data, "email")) {
data.email = data.fullName + randomize + data.email;
@@ -231,23 +316,19 @@ export class API {
if(hasProperty(data, 'domainName')) {
data.domainName = data.domainName + randomize;
}
- if(hasProperty(data, 'nodes')){
- if (data.nodes instanceof Array) {
- data.nodes.map(i => {
- if (typeof(i) === "object" && i !== null && hasProperty(i,
"cachegroup")) {
- i.cachegroup = i.cachegroup + randomize;
- }
- });
- }
+ if(hasProperty(data, 'nodes', "Array")){
+ data.nodes.map(i => {
+ if (typeof(i) === "object" && i !== null && hasProperty(i,
"cachegroup")) {
+ i.cachegroup = i.cachegroup + randomize;
+ }
+ });
}
- if(hasProperty(data, 'interfaces')){
- if (data.interfaces instanceof Array) {
- const ipv6 = randomIpv6();
- for (const inf of data.interfaces) {
- if (typeof(inf) === "object" && inf !== null &&
hasProperty(inf, "ipAddresses") && inf.ipAddresses instanceof Array) {
- for (const ip of inf.ipAddresses) {
- ip.address = ipv6.toString();
- }
+ if(hasProperty(data, 'interfaces', "Array")){
+ const ipv6 = randomIpv6();
+ for (const inf of data.interfaces) {
+ if (typeof(inf) === "object" && inf !== null &&
hasProperty(inf, "ipAddresses", "Array")) {
+ for (const ip of inf.ipAddresses) {
+ (ip as Record<"address", string>).address =
ipv6.toString();
}
}
}
@@ -273,7 +354,7 @@ export class API {
}
}
} else if (response.status == undefined) {
- throw new Error(`Error requesting ${this.config.params.apiUrl}:
${response}`);
+ throw new Error(`Error requesting ${this.apiURL}: ${response}`);
} else {
throw new Error(`Login failed: Response Status:
'${response.statusText}'' Response Data: '${response.data}'`);
}
diff --git a/traffic_portal/test/integration/CommonUtils/index.ts
b/traffic_portal/test/integration/CommonUtils/index.ts
new file mode 100644
index 0000000..1cd793c
--- /dev/null
+++ b/traffic_portal/test/integration/CommonUtils/index.ts
@@ -0,0 +1,21 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+export * from "./API";
+export * from "./utils";
diff --git a/traffic_portal/test/integration/CommonUtils/utils.ts
b/traffic_portal/test/integration/CommonUtils/utils.ts
new file mode 100644
index 0000000..5b3b6ac
--- /dev/null
+++ b/traffic_portal/test/integration/CommonUtils/utils.ts
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * hasProperty checks whether some variable passed as `o` has the string
+ * property `k`.
+ *
+ * @example
+ * hasProperty({}, "id", "string"); // returns false
+ * hasProperty({id: 8}, "id", "string"); // returns false
+ * hasProperty({id: undefined}, "id", "string"); // returns false
+ *
+ * @param o The object to check.
+ * @param k The key for which to check in the object.
+ * @param type Specifies that `o.k` should be a string.
+ * @returns Whether or not `o` has the string property `k`.
+ */
+export function hasProperty<T extends object, K extends PropertyKey>(o: T, k:
K, type: "string"): o is T & Record<K, string>;
+/**
+ * hasProperty checks whether some variable passed as `o` has the number
+ * property `k`.
+ *
+ * @example
+ * hasProperty({}, "id", "number"); // returns false
+ * hasProperty({id: 8}, "id", "number"); // returns true
+ * hasProperty({id: undefined}, "id", "number"); // returns false
+ *
+ * @param o The object to check.
+ * @param k The key for which to check in the object.
+ * @param type Specifies that `o.k` should be a number.
+ * @returns Whether or not `o` has the number property `k`.
+ */
+export function hasProperty<T extends object, K extends PropertyKey>(o: T, k:
K, type: "number"): o is T & Record<K, number>;
+/**
+ * hasProperty checks whether some variable passed as `o` has the boolean
+ * property `k`.
+ *
+ * @example
+ * hasProperty({}, "id", "boolean"); // returns false
+ * hasProperty({id: 8}, "id", "boolean"); // returns false
+ * hasProperty({id: undefined}, "id", "boolean"); // returns false
+ * hasProperty({id: true}, "id", "boolean"); // returns true
+ *
+ * @param o The object to check.
+ * @param k The key for which to check in the object.
+ * @param type Specifies that `o.k` should be a boolean.
+ * @returns Whether or not `o` has the boolean property `k`.
+ */
+export function hasProperty<T extends object, K extends PropertyKey>(o: T, k:
K, type: "boolean"): o is T & Record<K, boolean>;
+/**
+ * hasProperty checks whether some variable passed as `o` has the Array
property
+ * `k`.
+ *
+ * @example
+ * hasProperty({}, "id", "Array"); // returns false
+ * hasProperty({id: 8}, "id", "Array"); // returns false
+ * hasProperty({id: undefined}, "id", "Array"); // returns false
+ * hasProperty({id: []}, "id", "Array"); // returns true
+ * hasProperty({id: [undefined, null, -7, true]}, "id", "Array"); // returns
true
+ *
+ * @param o The object to check.
+ * @param k The key for which to check in the object.
+ * @param type Specifies that `o.k` should be a (potentially non-homogenous)
+ * Array.
+ * @returns Whether or not `o` has the Array property `k`.
+ */
+export function hasProperty<T extends object, K extends PropertyKey>(o: T, k:
K, type: "Array"): o is T & Record<K, Array<unknown>>;
+/**
+ * hasProperty checks, generically, whether some variable passed as `o` has the
+ * property `k`.
+ *
+ * @example
+ * hasProperty({}, "id"); // returns false
+ * hasProperty({id: 8}, "id"); // returns true
+ * hasProperty({id: undefined}, "id"); // returns true
+ *
+ * @param o The object to check.
+ * @param k The key for which to check in the object.
+ * @returns Whether or not `o` has the property `k`.
+ */
+export function hasProperty<T extends object, K extends PropertyKey>(o: T, k:
K): o is T & Record<K, unknown>;
+/**
+ * hasProperty checks, generically, whether some variable passed as `o` has the
+ * property `k`.
+ *
+ * @example
+ * hasProperty({}, "id"); // returns false
+ * hasProperty({id: 8}, "id"); // returns true
+ * hasProperty({id: undefined}, "id"); // returns true
+ * hasProperty({id: 8}, "id", "number"); // returns true
+ * hasProperty({id: undefined}, "id", "number"); // returns false
+ * hasProperty({id: 8}, "id", "string"); // returns false
+ *
+ * @param o The object to check.
+ * @param k The key for which to check in the object.
+ * @param type Optionally specify a type to check for.
+ * @returns Whether or not `o` has the property `k`.
+ */
+export function hasProperty<T extends object, K extends PropertyKey, S>(o: T,
k: K, type?: "string" | "number" | "boolean" | "Array"): o is T & Record<K, S> {
+ if (!Object.prototype.hasOwnProperty.call(o, k)) {
+ return false;
+ }
+ if (!type) {
+ return true;
+ }
+ const val = (o as Record<K, unknown>)[k];
+ switch (type) {
+ case "string":
+ return typeof(val) === "string";
+ case "number":
+ return typeof(val) === "number";
+ case "boolean":
+ return typeof(val) === "boolean";
+ case "Array":
+ return val instanceof Array;
+ }
+}
diff --git a/traffic_portal/test/integration/config.model.ts
b/traffic_portal/test/integration/config.model.ts
new file mode 100644
index 0000000..9895820
--- /dev/null
+++ b/traffic_portal/test/integration/config.model.ts
@@ -0,0 +1,165 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { hasProperty } from "./CommonUtils/utils";
+
+/** Possible levels of TO Alerts */
+export type AlertLevel = "success" | "info" | "warning" | "error";
+
+/**
+ * Checks whether an object is a valid Alert level.
+ *
+ * @param s The object to check.
+ * @returns Whether or not `s` is an AlertLevel.
+ */
+export function isAlertLevel(s: unknown): s is AlertLevel {
+ if (typeof(s) !== "string") {
+ return false;
+ }
+ switch (s) {
+ case "success":
+ case "info":
+ case "warning":
+ case "error":
+ return true;
+ }
+ return false;
+}
+
+/** TO API Alerts */
+export interface Alert {
+ level: AlertLevel;
+ text: string;
+}
+
+/**
+ * Checks whether an object is an Alert like the ones TO normally returns.
+ *
+ * @param a The object to check.
+ * @returns Whether or not `a` is an Alert.
+ */
+export function isAlert(a: unknown): a is Alert {
+ if (typeof(a) !== "object" || a === null) {
+ return false;
+ }
+ if (!hasProperty(a, "level") || !hasProperty(a, "text", "string")) {
+ return false;
+ }
+ return isAlertLevel(a.level);
+}
+
+/**
+ * Logs an alert to the appropriate console stream based on its `level`.
+ *
+ * @param a The Alert to log.
+ * @param prefix Optional prefix for the log message
+ */
+export function logAlert(a: Alert, prefix?: string): void {
+ let logfunc;
+ let pre = (prefix ?? "").trimStart();
+ switch (a.level) {
+ case "success":
+ logfunc = console.log;
+ pre = `SUCCESS: ${pre}`;
+ break;
+ case "info":
+ logfunc = console.info
+ pre = `INFO: ${pre}`;
+ break;
+ case "warning":
+ logfunc = console.warn
+ pre = `WARN: ${pre}`;
+ break;
+ case "error":
+ logfunc = console.error
+ pre = `ERROR: ${pre}`;
+ break;
+ }
+ logfunc(pre, a.text);
+}
+
+/** TestingConfig is the type of a testing configuration. */
+export interface TestingConfig {
+ /** This is login information for a user with admin-level permissions.
*/
+ readonly login: {
+ readonly password: string;
+ readonly username: string;
+ };
+ /** The URL at which the Traffic Ops API can be accessed. */
+ readonly apiUrl: string;
+ /** The URL at which Traffic Portal is served - root path. */
+ readonly baseUrl: string;
+ /** Logging alert levels that are enabled. */
+ readonly alertLevels?: Array<AlertLevel>;
+}
+
+/**
+ * Checks if a passed object is a valid testing configuration.
+ *
+ * @param c The object to check.
+ * @returns `true`, always. When the check fails, it throws an error that
+ * explains why.
+ */
+export function isTestingConfig(c: unknown): c is TestingConfig {
+ if (typeof(c) !== "object") {
+ throw new Error(`testing configuration must be an object, not a
'${typeof(c)}'`);
+ }
+ if (c === null) {
+ throw new Error("testing configuration must be an object, not
'null'");
+ }
+
+ if (!hasProperty(c, "login") || typeof(c.login) !== "object" || c.login
=== null) {
+ throw new Error("missing or invalid 'login' property");
+ }
+ if (!hasProperty(c.login, "password", "string") ||
!hasProperty(c.login, "username", "string")) {
+ throw new Error("'login' property has missing and/or invalid
'password' and/or 'username' property(ies)");
+ }
+ if (c.login.username === "" || c.login.password === "") {
+ throw new Error("neither 'login.username' nor 'login.password'
may be blank");
+ }
+ if (!hasProperty(c, "apiUrl", "string")) {
+ throw new Error("missing or invalid 'apiUrl' property");
+ }
+ try {
+ new URL(c.apiUrl);
+ } catch (e) {
+ throw new Error(`'apiUrl' is not a valid URL: ${c.apiUrl}`);
+ }
+ let baseURL;
+ if (!hasProperty(c, "baseUrl", "string")) {
+ throw new Error("missing or invalid 'baseUrl' property");
+ }
+ try {
+ baseURL = new URL(c.baseUrl);
+ } catch (e) {
+ throw new Error(`'baseUrl' is not a valid URL: ${c.baseUrl}`);
+ }
+ if (baseURL.pathname !== "/") {
+ throw new Error("'baseUrl' must be a root path");
+ }
+ if (!hasProperty(c, "alertLevels")) {
+ return true;
+ }
+ if (!(c.alertLevels instanceof Array)) {
+ throw new Error("'alertLevels' must be an array");
+ }
+ if (!c.alertLevels.every(isAlertLevel)) {
+ throw new Error(`invalid alert levels: ${c.alertLevels}`);
+ }
+ return true;
+}
diff --git a/traffic_portal/test/integration/config.ts
b/traffic_portal/test/integration/config.ts
index 96ccc55..388d14b 100644
--- a/traffic_portal/test/integration/config.ts
+++ b/traffic_portal/test/integration/config.ts
@@ -23,9 +23,10 @@ import { Config, browser } from 'protractor';
import { JUnitXmlReporter } from 'jasmine-reporters';
import HtmlReporter from "protractor-beautiful-reporter";
-import { API } from './CommonUtils/API';
+import { API } from './CommonUtils';
import * as conf from "./config.json"
import { prerequisites } from "./Data";
+import { isTestingConfig } from "./config.model";
const downloadsPath = resolve('Downloads');
export const randomize = Math.random().toString(36).substring(3, 7);
@@ -37,6 +38,23 @@ if (config.capabilities) {
} else {
config.capabilities = {chromeOptions: {prefs: {download: {default_directory:
downloadsPath}}}};
}
+
+if (!config.params) {
+ throw new Error("no testing parameters provided - cannot proceed");
+}
+
+try {
+ if (!isTestingConfig(config.params)) {
+ throw new Error();
+ }
+} catch (e) {
+ const msg = e instanceof Error ? e.message : String(e);
+ throw new Error(`invalid testing params: ${msg}`);
+}
+
+export const testingConfig = config.params;
+export const api = new API(testingConfig);
+
config.onPrepare = async function () {
await browser.waitForAngularEnabled(true);
await browser.driver.manage().window().maximize();
@@ -66,6 +84,5 @@ config.onPrepare = async function () {
}).getJasmine2Reporter());
}
- const api = new API();
await api.UseAPI(prerequisites);
}
diff --git a/traffic_portal/test/integration/specs/ASNs.spec.ts
b/traffic_portal/test/integration/specs/ASNs.spec.ts
index a3761c8..3359931 100644
--- a/traffic_portal/test/integration/specs/ASNs.spec.ts
+++ b/traffic_portal/test/integration/specs/ASNs.spec.ts
@@ -20,11 +20,10 @@ import { browser } from 'protractor';
import { LoginPage } from '../PageObjects/LoginPage.po';
import { TopNavigationPage } from '../PageObjects/TopNavigationPage.po';
-import { API } from '../CommonUtils/API';
+import { api } from "../config";
import { ASNsPage } from '../PageObjects/ASNs.po';
import { ASNs } from "../Data";
-const api = new API();
const loginPage = new LoginPage();
const topNavigation = new TopNavigationPage();
const asnsPage = new ASNsPage();
diff --git a/traffic_portal/test/integration/specs/Coordinates.spec.ts
b/traffic_portal/test/integration/specs/Coordinates.spec.ts
index cb6c924..a617130 100644
--- a/traffic_portal/test/integration/specs/Coordinates.spec.ts
+++ b/traffic_portal/test/integration/specs/Coordinates.spec.ts
@@ -20,11 +20,10 @@ import { browser } from 'protractor';
import { LoginPage } from '../PageObjects/LoginPage.po'
import { CoordinatesPage } from '../PageObjects/CoordinatesPage.po';
-import { API } from '../CommonUtils/API';
+import { api } from "../config";
import { TopNavigationPage } from '../PageObjects/TopNavigationPage.po';
import { coordinates } from "../Data";
-const api = new API();
const loginPage = new LoginPage();
const topNavigation = new TopNavigationPage();
const coordinatesPage = new CoordinatesPage();
diff --git a/traffic_portal/test/integration/specs/DeliveryServices.spec.ts
b/traffic_portal/test/integration/specs/DeliveryServices.spec.ts
index 2ba4a69..7c92742 100644
--- a/traffic_portal/test/integration/specs/DeliveryServices.spec.ts
+++ b/traffic_portal/test/integration/specs/DeliveryServices.spec.ts
@@ -21,10 +21,9 @@ import { browser } from 'protractor';
import { LoginPage } from '../PageObjects/LoginPage.po'
import { DeliveryServicePage } from '../PageObjects/DeliveryServicePage.po';
import { TopNavigationPage } from '../PageObjects/TopNavigationPage.po';
-import { API } from '../CommonUtils/API';
+import { api } from "../config";
import { deliveryservices } from "../Data/deliveryservices";
-const api = new API();
const topNavigation = new TopNavigationPage();
const loginPage = new LoginPage();
const deliveryservicesPage = new DeliveryServicePage();
@@ -66,7 +65,7 @@ deliveryservices.tests.forEach(async deliveryservicesData => {
expect(await
deliveryservicesPage.AssignServerToDeliveryService(assignserver)).toBe(true);
await deliveryservicesPage.OpenDeliveryServicePage();
}
-
+
)
})
deliveryservicesData.assignrequiredcapabilities.forEach(assignrc
=> {
diff --git a/traffic_portal/test/integration/specs/Divisions.spec.ts
b/traffic_portal/test/integration/specs/Divisions.spec.ts
index 5028c4e..27f5342 100644
--- a/traffic_portal/test/integration/specs/Divisions.spec.ts
+++ b/traffic_portal/test/integration/specs/Divisions.spec.ts
@@ -20,11 +20,11 @@ import { browser } from 'protractor';
import { LoginPage } from '../PageObjects/LoginPage.po';
import { TopNavigationPage } from '../PageObjects/TopNavigationPage.po';
-import { API } from '../CommonUtils/API';
+import { api } from "../config";
import { DivisionsPage } from '../PageObjects/Divisions.po';
import { divisions } from "../Data";
-const api = new API();
+
const loginPage = new LoginPage();
const topNavigation = new TopNavigationPage();
const divisionsPage = new DivisionsPage();
diff --git a/traffic_portal/test/integration/specs/Jobs.spec.ts
b/traffic_portal/test/integration/specs/Jobs.spec.ts
index ba7b8c1..9a0973a 100644
--- a/traffic_portal/test/integration/specs/Jobs.spec.ts
+++ b/traffic_portal/test/integration/specs/Jobs.spec.ts
@@ -18,13 +18,12 @@
*/
import { browser } from 'protractor';
-import { API } from '../CommonUtils/API';
+import { api } from "../config";
import { LoginPage } from '../PageObjects/LoginPage.po';
import { TopNavigationPage } from '../PageObjects/TopNavigationPage.po';
import { JobsPage } from '../PageObjects/Jobs.po'
import { jobs } from '../Data/jobs';
-const api = new API();
const loginPage = new LoginPage();
const topNavigation = new TopNavigationPage();
const jobsPage = new JobsPage();
diff --git a/traffic_portal/test/integration/specs/Origins.spec.ts
b/traffic_portal/test/integration/specs/Origins.spec.ts
index 20ead35..974be82 100644
--- a/traffic_portal/test/integration/specs/Origins.spec.ts
+++ b/traffic_portal/test/integration/specs/Origins.spec.ts
@@ -21,10 +21,9 @@ import { browser } from 'protractor';
import { LoginPage } from '../PageObjects/LoginPage.po'
import { OriginsPage } from '../PageObjects/OriginsPage.po';
import { TopNavigationPage } from '../PageObjects/TopNavigationPage.po';
-import { API } from '../CommonUtils/API';
+import { api } from "../config";
import { origins } from "../Data";
-const api = new API();
const loginPage = new LoginPage();
const topNavigation = new TopNavigationPage();
const originsPage = new OriginsPage();
diff --git a/traffic_portal/test/integration/specs/Parameters.spec.ts
b/traffic_portal/test/integration/specs/Parameters.spec.ts
index 95b8c59..04c6c6c 100644
--- a/traffic_portal/test/integration/specs/Parameters.spec.ts
+++ b/traffic_portal/test/integration/specs/Parameters.spec.ts
@@ -20,11 +20,10 @@ import { browser } from 'protractor';
import { LoginPage } from '../PageObjects/LoginPage.po'
import { ParametersPage } from '../PageObjects/ParametersPage.po';
-import { API } from '../CommonUtils/API';
+import { api } from "../config";
import { TopNavigationPage } from '../PageObjects/TopNavigationPage.po';
import { parameters } from "../Data";
-const api = new API();
const loginPage = new LoginPage();
const topNavigation = new TopNavigationPage();
const parametersPage = new ParametersPage();
@@ -63,7 +62,7 @@ parameters.tests.forEach(async parametersData => {
expect(await
parametersPage.ToggleTableColumn(toggle.Name)).toBe(true);
await parametersPage.OpenParametersPage();
}
-
+
});
})
parametersData.add.forEach(add => {
diff --git a/traffic_portal/test/integration/specs/PhysLocations.spec.ts
b/traffic_portal/test/integration/specs/PhysLocations.spec.ts
index 24bed71..c221fb8 100644
--- a/traffic_portal/test/integration/specs/PhysLocations.spec.ts
+++ b/traffic_portal/test/integration/specs/PhysLocations.spec.ts
@@ -20,11 +20,10 @@ import { browser } from 'protractor';
import { LoginPage } from '../PageObjects/LoginPage.po'
import { PhysLocationsPage } from '../PageObjects/PhysLocationsPage.po';
-import { API } from '../CommonUtils/API';
+import { api } from "../config";
import { TopNavigationPage } from '../PageObjects/TopNavigationPage.po';
import { physLocations } from "../Data";
-const api = new API();
const loginPage = new LoginPage();
const topNavigation = new TopNavigationPage();
const physlocationsPage = new PhysLocationsPage();
diff --git a/traffic_portal/test/integration/specs/Profiles.spec.ts
b/traffic_portal/test/integration/specs/Profiles.spec.ts
index 5878b85..f415db0 100644
--- a/traffic_portal/test/integration/specs/Profiles.spec.ts
+++ b/traffic_portal/test/integration/specs/Profiles.spec.ts
@@ -21,10 +21,9 @@ import { browser } from 'protractor'
import { LoginPage } from '../PageObjects/LoginPage.po'
import { ProfilesPage } from '../PageObjects/ProfilesPage.po';
import { TopNavigationPage } from '../PageObjects/TopNavigationPage.po';
-import { API } from '../CommonUtils/API';
+import { api } from "../config";
import { profiles } from "../Data";
-const api = new API();
const loginPage = new LoginPage();
const topNavigation = new TopNavigationPage();
const profilesPage = new ProfilesPage();
diff --git a/traffic_portal/test/integration/specs/Regions.spec.ts
b/traffic_portal/test/integration/specs/Regions.spec.ts
index 4380c7a..82b768e 100644
--- a/traffic_portal/test/integration/specs/Regions.spec.ts
+++ b/traffic_portal/test/integration/specs/Regions.spec.ts
@@ -20,11 +20,10 @@ import { browser } from 'protractor';
import { LoginPage } from '../PageObjects/LoginPage.po';
import { TopNavigationPage } from '../PageObjects/TopNavigationPage.po';
-import { API } from '../CommonUtils/API';
+import { api } from "../config";
import { RegionsPage } from '../PageObjects/RegionsPage.po';
import { regions } from "../Data";
-const api = new API();
const loginPage = new LoginPage();
const topNavigation = new TopNavigationPage();
const regionsPage = new RegionsPage();
diff --git
a/traffic_portal/test/integration/specs/ServerServerCapabilities.spec.ts
b/traffic_portal/test/integration/specs/ServerServerCapabilities.spec.ts
index c3efb2f..d21c6d9 100644
--- a/traffic_portal/test/integration/specs/ServerServerCapabilities.spec.ts
+++ b/traffic_portal/test/integration/specs/ServerServerCapabilities.spec.ts
@@ -22,10 +22,9 @@ import { LoginPage } from '../PageObjects/LoginPage.po'
import { ServerCapabilitiesPage } from
'../PageObjects/ServerCapabilitiesPage.po';
import { TopNavigationPage } from '../PageObjects/TopNavigationPage.po';
import { ServersPage } from '../PageObjects/ServersPage.po';
-import { API } from '../CommonUtils/API';
+import { api } from "../config";
import { serverServerCapabilities } from "../Data";
-const api = new API();
const loginPage = new LoginPage();
const topNavigation = new TopNavigationPage();
const serverCapabilitiesPage = new ServerCapabilitiesPage();
diff --git a/traffic_portal/test/integration/specs/Servers.spec.ts
b/traffic_portal/test/integration/specs/Servers.spec.ts
index 536a166..867397e 100644
--- a/traffic_portal/test/integration/specs/Servers.spec.ts
+++ b/traffic_portal/test/integration/specs/Servers.spec.ts
@@ -20,11 +20,10 @@ import { browser } from 'protractor';
import { LoginPage } from '../PageObjects/LoginPage.po'
import { ServersPage } from '../PageObjects/ServersPage.po';
-import { API } from '../CommonUtils/API';
+import { api } from "../config";
import { TopNavigationPage } from '../PageObjects/TopNavigationPage.po';
import { servers } from "../Data";
-const api = new API();
const loginPage = new LoginPage();
const topNavigation = new TopNavigationPage();
const serversPage = new ServersPage();
diff --git a/traffic_portal/test/integration/specs/ServiceCategories.spec.ts
b/traffic_portal/test/integration/specs/ServiceCategories.spec.ts
index 131658d..fdbcd6b 100644
--- a/traffic_portal/test/integration/specs/ServiceCategories.spec.ts
+++ b/traffic_portal/test/integration/specs/ServiceCategories.spec.ts
@@ -20,11 +20,10 @@ import { browser } from 'protractor';
import { LoginPage } from '../PageObjects/LoginPage.po';
import { TopNavigationPage } from '../PageObjects/TopNavigationPage.po';
-import { API } from '../CommonUtils/API';
+import { api } from "../config";
import { ServiceCategoriesPage } from '../PageObjects/ServiceCategories.po';
import { serviceCategories } from "../Data";
-const api = new API();
const loginPage = new LoginPage();
const topNavigation = new TopNavigationPage();
const serviceCategoriesPage = new ServiceCategoriesPage();
diff --git a/traffic_portal/test/integration/specs/Statuses.spec.ts
b/traffic_portal/test/integration/specs/Statuses.spec.ts
index bb3ac1b..5f070c3 100644
--- a/traffic_portal/test/integration/specs/Statuses.spec.ts
+++ b/traffic_portal/test/integration/specs/Statuses.spec.ts
@@ -20,11 +20,10 @@ import { browser } from 'protractor';
import { LoginPage } from '../PageObjects/LoginPage.po';
import { TopNavigationPage } from '../PageObjects/TopNavigationPage.po';
-import { API } from '../CommonUtils/API';
+import { api } from "../config";
import { StatusesPage } from '../PageObjects/Statuses.po'
import { statuses } from "../Data";
-const api = new API();
const loginPage = new LoginPage();
const topNavigation = new TopNavigationPage();
const statusesPage = new StatusesPage();
diff --git a/traffic_portal/test/integration/specs/Topologies.spec.ts
b/traffic_portal/test/integration/specs/Topologies.spec.ts
index b9d9ccb..89678ab 100644
--- a/traffic_portal/test/integration/specs/Topologies.spec.ts
+++ b/traffic_portal/test/integration/specs/Topologies.spec.ts
@@ -20,11 +20,10 @@
import { browser } from 'protractor';
import { LoginPage } from '../PageObjects/LoginPage.po';
import { TopNavigationPage } from '../PageObjects/TopNavigationPage.po';
-import { API } from '../CommonUtils/API';
+import { api } from "../config";
import { TopologiesPage } from '../PageObjects/TopologiesPage.po';
import { topologies } from "../Data/topologies";
-const api = new API();
const loginPage = new LoginPage();
const topologiesPage = new TopologiesPage();
const topNavigation = new TopNavigationPage();
@@ -56,7 +55,7 @@ topologies.tests.forEach(async topologiesData =>{
it('can logout', async function(){
expect(await topNavigation.Logout()).toBeTruthy();
})
-
+
})
})
})
@@ -66,4 +65,3 @@ describe('Clean Up API for Topologies Test', () => {
await api.UseAPI(topologies.cleanup);
});
});
-
diff --git a/traffic_portal/test/integration/specs/Types.spec.ts
b/traffic_portal/test/integration/specs/Types.spec.ts
index 580635d..b9fb5d6 100644
--- a/traffic_portal/test/integration/specs/Types.spec.ts
+++ b/traffic_portal/test/integration/specs/Types.spec.ts
@@ -20,11 +20,10 @@ import { browser } from 'protractor';
import { LoginPage } from '../PageObjects/LoginPage.po';
import { TopNavigationPage } from '../PageObjects/TopNavigationPage.po';
-import { API } from '../CommonUtils/API';
+import { api } from "../config";
import { TypesPage } from '../PageObjects/Types.po'
import { types } from "../Data";
-const api = new API();
const loginPage = new LoginPage();
const topNavigation = new TopNavigationPage();
const typesPage = new TypesPage();