experimental/traffic-portal/cypress.config.ts (341 lines of code) (raw):

/* * Licensed 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 { promises as fs } from "fs"; import * as https from "https"; import axios, { AxiosError } from "axios"; import { defineConfig } from "cypress"; import type { CDN, LoginRequest, ProfileType, RequestASN, RequestCacheGroup, RequestCoordinate, RequestDeliveryService, RequestDivision, RequestParameter, RequestPhysicalLocation, RequestProfile, RequestRegion, RequestRole, RequestServer, RequestServerCapability, RequestStatus, RequestSteeringTarget, RequestTenant, RequestType, ResponseCacheGroup, ResponseDeliveryService, ResponseDivision, ResponseParameter, ResponsePhysicalLocation, ResponseProfile, ResponseRegion, ResponseStatus, TypeFromResponse } from "trafficops-types"; import type { CreatedData } from "./cypress/support/testing.data"; import PluginEvents = Cypress.PluginEvents; /** * Creates mock data needed for E2E testing. * * Ideally this functionality would go in a different file, but for some reason * Cypress gets very upset if you import anything but a type from any TypeScript * file in this config file. * * @param toURL The URL of a Traffic Ops instance e.g. 'https://traffic.ops/'. * @param apiVersion The version of the API to use e.g. '4.1'. * @param adminUser The username of the 'admin' Role user that will be used * to set up testing data. * @param adminPass The password of the 'admin' Role user that will be used to * set up testing data. * @returns The data that was created and the unique string that was appended to * all names that are required to be unique. */ async function createData(toURL: string, apiVersion: string, adminUser: string, adminPass: string): Promise<CreatedData> { const apiUrl = `${toURL}/api/${apiVersion}`; const client = axios.create({ httpsAgent: new https.Agent({ rejectUnauthorized: false }) }); if (!Object.keys(client.defaults.headers.common).includes("Cookie")) { let accessToken = ""; const loginReq: LoginRequest = { p: adminPass, u: adminUser }; try { const logResp = await client.post(`${apiUrl}/user/login`, JSON.stringify(loginReq)); if (logResp.headers["set-cookie"]) { for (const cookie of logResp.headers["set-cookie"]) { if (cookie.includes("access_token")) { accessToken = cookie; break; } } } } catch (e) { // eslint-disable-next-line no-console console.error((e as AxiosError).message); throw e; } if (accessToken === "") { const e = new Error("Access token is not set"); // eslint-disable-next-line no-console console.error(e.message); throw e; } // eslint-disable-next-line @typescript-eslint/naming-convention client.defaults.headers.common = {Cookie: accessToken}; } let resp = await client.get(`${apiUrl}/types`); const types: Array<TypeFromResponse> = resp.data.response; const httpType = types.find(typ => typ.name === "HTTP" && typ.useInTable === "deliveryservice"); if (!httpType) { throw new Error("Unable to find `HTTP` type"); } const steeringType = types.find(typ => typ.name === "STEERING" && typ.useInTable === "deliveryservice"); if (!steeringType) { throw new Error("Unable to find `STEERING` type"); } const steeringWeightType = types.find(typ => typ.name === "STEERING_WEIGHT" && typ.useInTable === "steering_target"); if (!steeringWeightType) { throw new Error("Unable to find `STEERING_WEIGHT` type"); } const cgType = types.find(typ => typ.useInTable === "cachegroup"); if (!cgType) { throw new Error("Unable to find any Cache Group Types"); } const edgeType = types.find(typ => typ.useInTable === "server" && typ.name === "EDGE"); if (!edgeType) { throw new Error("Unable to find `EDGE` type"); } const id = (new Date()).getTime().toString(); const data = { uniqueString: id } as CreatedData; let url = `${apiUrl}/cdns`; try { const cdn: CDN = { dnssecEnabled: false, domainName: `tests${id}.com`, name: `testCDN${id}` }; resp = await client.post(url, JSON.stringify(cdn)); const respCDN = resp.data.response; data.cdn = respCDN; const ds: RequestDeliveryService = { active: false, cacheurl: null, cdnId: respCDN.id, displayName: `test DS${id}`, dscp: 0, ecsEnabled: false, edgeHeaderRewrite: null, fqPacingRate: null, geoLimit: 0, geoProvider: 0, httpBypassFqdn: null, infoUrl: null, initialDispersion: 1, ipv6RoutingEnabled: false, logsEnabled: false, maxOriginConnections: 0, maxRequestHeaderBytes: 0, midHeaderRewrite: null, missLat: 0, missLong: 0, multiSiteOrigin: false, orgServerFqdn: "http://test.com", profileId: 1, protocol: 0, qstringIgnore: 0, rangeRequestHandling: 0, regionalGeoBlocking: false, remapText: null, routingName: "test", signed: false, tenantId: 1, typeId: httpType.id, xmlId: `testDS${id}` }; url = `${apiUrl}/deliveryservices`; resp = await client.post(url, JSON.stringify(ds)); let respDS: ResponseDeliveryService = resp.data.response[0]; data.ds = respDS; ds.displayName = `test DS2${id}`; ds.xmlId = `testDS2${id}`; resp = await client.post(url, JSON.stringify(ds)); respDS = resp.data.response[0]; data.ds2 = respDS; ds.displayName = `test steering DS${id}`; ds.xmlId = `testSDS${id}`; ds.typeId = steeringType.id; resp = await client.post(url, JSON.stringify(ds)); respDS = resp.data.response[0]; data.steeringDS = respDS; const target: RequestSteeringTarget = { targetId: data.ds.id, typeId: steeringWeightType.id, value: 1 }; url = `${apiUrl}/steering/${data.steeringDS.id}/targets`; await client.post(url, JSON.stringify(target)); target.targetId = data.ds2.id; await client.post(url, JSON.stringify(target)); const tenant: RequestTenant = { active: true, name: `testT${id}`, parentId: 1 }; url = `${apiUrl}/tenants`; resp = await client.post(url, JSON.stringify(tenant)); data.tenant = resp.data.response; const division: RequestDivision = { name: `testD${id}` }; url = `${apiUrl}/divisions`; resp = await client.post(url, JSON.stringify(division)); const respDivision: ResponseDivision = resp.data.response; data.division = respDivision; const region: RequestRegion = { division: respDivision.id, name: `testR${id}` }; url = `${apiUrl}/regions`; resp = await client.post(url, JSON.stringify(region)); const respRegion: ResponseRegion = resp.data.response; data.region = respRegion; const cacheGroup: RequestCacheGroup = { name: `test${id}`, shortName: `test${id}`, typeId: cgType.id }; url = `${apiUrl}/cachegroups`; resp = await client.post(url, JSON.stringify(cacheGroup)); const responseCG: ResponseCacheGroup = resp.data.response; data.cacheGroup = responseCG; const asn: RequestASN = { asn: +id, cachegroupId: responseCG.id }; url = `${apiUrl}/asns`; resp = await client.post(url, JSON.stringify(asn)); data.asn = resp.data.response; const physLoc: RequestPhysicalLocation = { address: "street", city: "city", comments: "someone set us up the bomb", email: "email@test.com", name: `phys${id}`, phone: "111-867-5309", poc: "me", regionId: respRegion.id, shortName: `short${id}`, state: "CA", zip: "80000" }; url = `${apiUrl}/phys_locations`; resp = await client.post(url, JSON.stringify(physLoc)); const respPhysLoc: ResponsePhysicalLocation = resp.data.response; respPhysLoc.region = respRegion.name; data.physLoc = respPhysLoc; const coordinate: RequestCoordinate = { latitude: 0, longitude: 0, name: `coord${id}` }; url = `${apiUrl}/coordinates`; resp = await client.post(url, JSON.stringify(coordinate)); data.coordinate = resp.data.response; const type: RequestType = { description: "blah", name: `type${id}`, useInTable: "server" }; url = `${apiUrl}/types`; resp = await client.post(url, JSON.stringify(type)); data.type = resp.data.response; const status: RequestStatus = { description: "blah", name: `status${id}`, }; url = `${apiUrl}/statuses`; resp = await client.post(url, JSON.stringify(status)); const respStatus: ResponseStatus = resp.data.response; data.status = respStatus; const profile: RequestProfile = { cdn: respCDN.id, description: "blah", name: `profile${id}`, routingDisabled: false, type: "ATS_PROFILE" as ProfileType, }; url = `${apiUrl}/profiles`; resp = await client.post(url, JSON.stringify(profile)); const respProfile: ResponseProfile = resp.data.response; data.profile = respProfile; const parameter: RequestParameter = { configFile: "cfg.txt", name: `param${id}`, secure: false, value: "10", }; url = `${apiUrl}/parameters`; resp = await client.post(url, JSON.stringify(parameter)); const responseParameter: ResponseParameter = resp.data.response; data.parameter = responseParameter; const server: RequestServer = { cachegroupId: responseCG.id, cdnId: respCDN.id, domainName: "domain.com", hostName: id, interfaces: [{ ipAddresses: [{ address: "192.160.1.0", gateway: null, serviceAddress: true }], maxBandwidth: 0, monitor: true, mtu: 1500, name: "eth0" }], physLocationId: respPhysLoc.id, profileNames: [respProfile.name], statusId: respStatus.id, typeId: edgeType.id }; url = `${apiUrl}/servers`; resp = await client.post(url, JSON.stringify(server)); data.edgeServer = resp.data.response; const capability: RequestServerCapability = { name: `test${id}` }; url = `${apiUrl}/server_capabilities`; resp = await client.post(url, JSON.stringify(capability)); data.capability = resp.data.response; const role: RequestRole = { description: "Has access to everything - cannot be modified or deleted", name: `admin${id}`, permissions: [ "ALL" ] }; url = `${apiUrl}/roles`; resp = await client.post(url, JSON.stringify(role)); data.role = resp.data.response; } catch (e) { const ae = e as AxiosError; ae.message = `Request (${ae.config?.method}) failed to ${url}`; ae.message += ae.response ? ` with response code ${ae.response.status}` : " with no response"; throw ae; } return data; } /** The schema for the Traffic Ops connection configuration file. */ interface TOConfig { adminPass: string; adminUser: string; apiVersion: string; toURL: string; } export default defineConfig({ component: { devServer: { bundler: "webpack", framework: "angular", }, specPattern: "**/*.cy.ts" }, e2e: { baseUrl: "http://localhost:4200", setupNodeEvents(on: PluginEvents) { on("before:run", async () => { const toConfig: TOConfig = JSON.parse(await fs.readFile("cypress/fixtures/to.config.json", {encoding: "utf-8"})); const data = await createData(toConfig.toURL, toConfig.apiVersion, toConfig.adminUser, toConfig.adminPass); let formattedData = JSON.stringify(data, null, "\t"); formattedData += "\n"; return fs.writeFile("cypress/fixtures/test.data.json", formattedData); }); }, }, experimentalInteractiveRunEvents: true, });