Skip to content

Commit

Permalink
E2E tests (#2061)
Browse files Browse the repository at this point in the history
* Add run e2e test on pipeline
* Create mock server per each test
* Create server and close server move to utils
* Add api list tests
* Change workflow command

Co-authored-by: Farhad Alizada <falizada@microsoft.com>
  • Loading branch information
JMach1 and Farhad Alizada authored Feb 17, 2023
1 parent 40d19b8 commit a468b81
Show file tree
Hide file tree
Showing 33 changed files with 3,410 additions and 150 deletions.
13 changes: 13 additions & 0 deletions .github/scripts/wait-for-server.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
param ($HostName = $(throw "HostName parameter is required."))
$Counter = 0
while ($Counter -ne 2 ){
Start-Sleep -Seconds 4
$Response = Invoke-WebRequest -Uri $HostName
if ($Response.StatusCode -eq 200){
Write-Host "server returned 200"
exit 0
}
$Counter++
}

Write-Error "Server didn't return 200 status"
24 changes: 24 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,27 @@ jobs:

- name: Run tests
run: npm run test

end2end-tests:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v3

- name: Install
run: npm install

- name: Build static data
run: npm run build-mock-static-data

- name: Start mock server
run: npm run serve-website &

- name: Wait for the server
run: ./.github/scripts/wait-for-server.ps1 -HostName "http://localhost:8080"
shell: pwsh

- name: Run tests
run: npm run test-e2e

5 changes: 3 additions & 2 deletions environmentConstants.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@

const staticDataEnvironment = "staticData";

const mockStaticDataEnvironment = "mockStaticData";
module.exports = {
staticDataEnvironment
staticDataEnvironment,
mockStaticDataEnvironment
}
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@
"build-publisher": "webpack --config webpack.publisher.js",
"build-runtime": "webpack --config webpack.runtime.js",
"build-function": "webpack --config webpack.function.js",
"test-e2e": "node node_modules/mocha/bin/_mocha -r mocha.js tests/e2e/**/*.spec.ts --timeout 3000000",
"test": "node node_modules/mocha/bin/_mocha -r mocha.js src/**/*.spec.ts",
"test-e2e": "node node_modules/mocha/bin/_mocha -r mocha.js tests/e2e/**/*.spec.ts --timeout 30000",
"deploy-function": "npm run build-function && cd dist/function && func azure functionapp publish < function app name >",
"publish": "webpack --config webpack.publisher.js && node dist/publisher/index.js && npm run serve-website",
"serve-website": "webpack serve --open --static ./dist/website --no-stats",
"build-mock-static-data": "webpack --config webpack.mockStaticData.js && node dist/publisher/index.js",
"build-static-data": "webpack --config webpack.staticData.js && node dist/publisher/index.js",
"serve-static-website": "npm run build-static-data && npm run serve-website",
"lint": "eslint src/**/*.ts",
"lint:fix": "eslint src/**/*.ts --fix"
},
Expand Down
7 changes: 3 additions & 4 deletions src/apim.publish.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ import { CustomHtmlPublishModule } from "./components/custom-html/customHtml.pub
import { CustomWidgetPublishModule } from "./components/custom-widget/customWidget.publish.module";
import { StaticDataHttpClient } from "./services/staticDataHttpClient";
import { PublisherStaticDataProvider } from "./services/publisherStaticDataProvider";
import { staticDataEnvironment } from "./../environmentConstants";
import { staticDataEnvironment,mockStaticDataEnvironment } from "./../environmentConstants";

export class ApimPublishModule implements IInjectorModule {
public register(injector: IInjector): void {
Expand Down Expand Up @@ -86,11 +86,10 @@ export class ApimPublishModule implements IInjectorModule {
injector.bindSingleton("runtimeConfigBuilder", RuntimeConfigBuilder);
injector.bindToCollection("publishers", AadConfigPublisher);
injector.bindToCollection("publishers", RuntimeConfigPublisher);

if (process.env.NODE_ENV === staticDataEnvironment) {
if (process.env.NODE_ENV === staticDataEnvironment || process.env.NODE_ENV === mockStaticDataEnvironment) {
injector.bind("httpClient", StaticDataHttpClient);
injector.bind("dataProvider", PublisherStaticDataProvider);
}

}
}
34 changes: 7 additions & 27 deletions src/config.validate.json
Original file line number Diff line number Diff line change
@@ -1,32 +1,12 @@
{
"environment": "validation",
"root": "http://localhost:8080",
"urls": {
"home": "https://contoso.com",
"signin": "https://contoso.com/signin",
"signup": "https://contoso.com/signup"
},
"signin": {
"firstName": "John",
"lastName": "Doe",
"credentials": {
"basic": {
"email": "johndoe@contoso.com",
"password": "< password >"
},
"aadB2C": {
"email": "johndoe@contoso.com",
"password": "< password >"
}
}
},
"signup": {
"firstName": "John",
"lastName": "Doe",
"credentials": {
"basic": {
"email": "johndoe@contoso.com",
"password": "< password >"
}
}
"home": "/",
"signin": "/signin",
"signup": "/signup",
"profile": "/profile",
"products": "/products",
"apis": "/apis"
}
}
6 changes: 6 additions & 0 deletions src/config.validate.publish.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"environment": "publishing",
"managementApiUrl": "http://localhost:8181/subscriptions/sid/resourceGroups/rgid/providers/Microsoft.ApiManagement/service/sid",
"managementApiAccessToken": "SharedAccessSignature...",
"useHipCaptcha": false
}
5 changes: 5 additions & 0 deletions src/config.validate.runtime.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"environment": "runtime",
"managementApiUrl": "http://localhost:8181/subscriptions/sid/resourceGroups/rgid/providers/Microsoft.ApiManagement/service/sid",
"backendUrl": "http://localhost:8080/"
}
4 changes: 2 additions & 2 deletions src/services/usersService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,15 @@ export class UsersService {
const userId = await this.authenticate(credentials);

if (userId) {
return; // successul authentication
return; // successful authentication
}

this.authenticator.clearAccessToken();
throw new UnauthorizedError("Please provide a valid email and password.");
}

/**
* Authenticates user with specified credentilas and returns user identifier.
* Authenticates user with specified credentials and returns user identifier.
* @param credentials {string} User credentials passed in "Authorization" header.
* @returns {string} User identifier.
*/
Expand Down
4 changes: 2 additions & 2 deletions src/startup.publish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { CorePublishModule } from "@paperbits/core/core.publish.module";
import { FormsModule } from "@paperbits/forms/forms.module";
import { ProseMirrorModule } from "@paperbits/prosemirror/prosemirror.module";
import { StylePublishModule } from "@paperbits/styles/styles.publish.module";
import { staticDataEnvironment } from "./../environmentConstants";
import { staticDataEnvironment, mockStaticDataEnvironment } from "./../environmentConstants";
import { ApimPublishModule } from "./apim.publish.module";
import { FileSystemBlobStorage } from "./components/filesystemBlobStorage";
import { StaticSettingsProvider } from "./components/staticSettingsProvider";
Expand All @@ -17,7 +17,7 @@ import { PublishingCacheModule } from "./persistence/publishingCacheModule";
/* Reading settings from configuration file */
let settingsProvider: ISettingsProvider;

if (process.env.NODE_ENV === staticDataEnvironment) {
if (process.env.NODE_ENV === staticDataEnvironment || process.env.NODE_ENV === mockStaticDataEnvironment) {
settingsProvider = new StaticSettingsProvider({
environment: "publishing",
managementApiUrl: "https://contoso.management.azure-api.net",
Expand Down
2 changes: 1 addition & 1 deletion src/startup.runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ injector.bindModule(new StyleRuntimeModule());
injector.bindModule(new ApimRuntimeModule());

document.addEventListener("DOMContentLoaded", () => {
if (process.env.NODE_ENV === staticDataEnvironment) {
if (process.env.NODE_ENV === staticDataEnvironment && process.env.ACCESS_TOKEN) {
sessionStorage.setItem("accessToken", process.env.ACCESS_TOKEN);
}

Expand Down
6 changes: 4 additions & 2 deletions tests/constants.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { LaunchOptions, BrowserLaunchArgumentOptions, BrowserConnectOptions } from "puppeteer";

export const BrowserLaunchOptions: LaunchOptions & BrowserLaunchArgumentOptions & BrowserConnectOptions = {
headless: false,
headless: true,
ignoreHTTPSErrors: true,
product: "chrome"
product: "chrome",
devtools: true,
userDataDir: "/puppeteer-data-dir", // necessary for persistent user preferences
};
10 changes: 10 additions & 0 deletions tests/e2e/maps/apis.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Page } from "puppeteer";

export class ApisWidget {
constructor(private readonly page: Page) { }

public async apis(): Promise<void> {
await this.page.waitForSelector("api-list.block");
await this.page.waitForSelector("api-list div.table div.table-body div.table-row");
}
}
10 changes: 10 additions & 0 deletions tests/e2e/maps/products.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Page } from "puppeteer";

export class ProductseWidget {
constructor(private readonly page: Page) { }

public async products(): Promise<void> {
await this.page.waitForSelector("product-list-runtime.block");
await this.page.waitForSelector("product-list-runtime div.table div.table-body div.table-row");
}
}
10 changes: 10 additions & 0 deletions tests/e2e/maps/profile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Page } from "puppeteer";

export class ProfileWidget {
constructor(private readonly page: Page) { }

public async profile(): Promise<void> {
await this.page.waitForSelector("profile-runtime .row");
await this.page.waitForSelector("subscriptions-runtime .table-row");
}
}
9 changes: 4 additions & 5 deletions tests/e2e/maps/signin-basic.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { Page } from "puppeteer";
import { User } from "../../mocks/user";

export class SigninBasicWidget {
export class SignInBasicWidget {
constructor(private readonly page: Page) { }

public async signInWithBasic(user: User): Promise<void> {
await this.page.type("#email", user.email);
await this.page.type("#password", user.password);
public async signInWithBasic(): Promise<void> {
await this.page.type("#email", "foo@bar.com");
await this.page.type("#password", "password");
await this.page.click("#signin");
await this.page.waitForNavigation({ waitUntil: "domcontentloaded" });
}
Expand Down
11 changes: 6 additions & 5 deletions tests/e2e/maps/signin-social.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
import { Page } from "puppeteer";
import { User } from "../../mocks";

export class SigninSocialWidget {
export class SignInSocialWidget {
constructor(private readonly page: Page) { }

public async signInWitAadB2C(user: User): Promise<void> {
public async signInWitAadB2C(): Promise<void> {
await this.page.click("#signinB2C");

await new Promise<void>((resolve) => {
this.page.once("popup", async (popup) => {
await popup.waitForSelector("[type=email]");
await popup.type("[type=email]", user.email);
await popup.type("[type=password]", user.password);
await popup.type("[type=email]", "foo@bar.com");
await popup.type("[type=password]", "password");
await popup.click("#next");

popup.on("close", () => resolve());

await new Promise(resolve => setTimeout(resolve, 50000000)); // just long wait
});
});

Expand Down
13 changes: 6 additions & 7 deletions tests/e2e/maps/signup-basic.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import { Page } from "puppeteer";
import { User } from "../../mocks/user";

export class SignupBasicWidget {
constructor(private readonly page: Page) { }

public async signUpWithBasic(user: User): Promise<void> {
await this.page.type("#email", user.email);
await this.page.type("#password", user.password);
await this.page.type("#confirmPassword", user.password);
await this.page.type("#firstName", user.firstName);
await this.page.type("#lastName", user.lastName);
public async signUpWithBasic(): Promise<void> {
await this.page.type("#email", "foo@bar.com");
await this.page.type("#password", "password");
await this.page.type("#confirmPassword", "password");
await this.page.type("#firstName", "Foo");
await this.page.type("#lastName", "Bar");
await this.page.click("#signup");

await this.page.waitForSelector("#confirmationMessage");
Expand Down
39 changes: 39 additions & 0 deletions tests/e2e/runtime/apis.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import * as puppeteer from "puppeteer";
import { expect } from "chai";
import { Utils } from "../../utils";
import { BrowserLaunchOptions } from "../../constants";
import { Server } from "http";
import { Apis } from "../../mocks/collection/apis";
import { Api } from "../../mocks/collection/api";
import { ApisWidget } from "../maps/apis";

describe("Apis page", async () => {
let config;
let browser: puppeteer.Browser;
let server: Server;

before(async () => {
config = await Utils.getConfig();
browser = await puppeteer.launch(BrowserLaunchOptions);
});
after(async () => {
browser.close();
Utils.closeServer(server);
});

it("User can see apis on the page", async () => {
var apis = new Apis();
apis.addApi(Api.getEchoApi());
server = await Utils.createMockServer([apis.getApisListResponse()]);

const page = await browser.newPage();
await page.goto(config.urls.apis);

const apiWidget = new ApisWidget(page);
await apiWidget.apis();

expect(await page.evaluate(() =>
document.querySelector("api-list div.table div.table-body div.table-row")?.parentElement?.childElementCount
)).to.equal(apis.apiList.length);
});
});
41 changes: 41 additions & 0 deletions tests/e2e/runtime/products.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import * as puppeteer from "puppeteer";
import { expect } from "chai";
import { Utils } from "../../utils";
import { BrowserLaunchOptions } from "../../constants";
import { Server } from "http";
import { Products } from "../../mocks/collection/products";
import { Product } from "../../mocks/collection/product";
import { ProductseWidget } from "../maps/products";

describe("Products page", async () => {
let config;
let browser: puppeteer.Browser;
let server: Server;

before(async () => {
config = await Utils.getConfig();
browser = await puppeteer.launch(BrowserLaunchOptions);
});
after(async () => {
browser.close();
Utils.closeServer(server);
});

it("User can see producst on the page", async () => {
var products = new Products();
products.addProduct(Product.getStartedProduct());
products.addProduct(Product.getUnlimitedProduct());

server = await Utils.createMockServer([products.getProductListResponse()]);

const page = await browser.newPage();
await page.goto(config.urls.products);

const productWidget = new ProductseWidget(page);
await productWidget.products();

expect(await page.evaluate(() =>
document.querySelector("product-list-runtime div.table div.table-body div.table-row")?.parentElement?.childElementCount
)).to.equal(products.productList.length);
});
});
Loading

0 comments on commit a468b81

Please sign in to comment.