Skip to content

Commit

Permalink
EA-1204 sample ts app and tests (#57)
Browse files Browse the repository at this point in the history
* Initial commit

* Add dotenv dependency; add .env to gitignore; get default key and oauth creds from .env file that is not checked in

* Add .env.example and info about the .env file in README.md

* Use next version in TS sample app; update code to handle changes; update tests; add initialize tests

* Remove logging

* Actually call initialize() when the button is pressed

* Add a test for successful authentication
  • Loading branch information
kylewadebwell authored May 28, 2024
1 parent a4affe9 commit 8284de0
Show file tree
Hide file tree
Showing 34 changed files with 5,179 additions and 1 deletion.
12 changes: 11 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,12 @@
**/.DS_Store
.idea/**
.idea/**

node_modules

playwright-report

test-results

.env

.yalc
27 changes: 27 additions & 0 deletions bwell-typescript-react/.devcontainer/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Dockerfile
# Use the official Node.js 21 image.
# https://hub.docker.com/_/node
FROM node:21

# Create and change to the app directory.
WORKDIR /usr/src/app

# Install production dependencies.
RUN apt-get update && apt-get install -y \
wget \
unzip \
libxss1 \
libappindicator1 \
libindicator7 \
libasound2 \
libnss3 \
libxtst6 \
xauth \
xvfb \
libgbm-dev \
--no-install-recommends \
&& wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb \
&& dpkg -i google-chrome-stable_current_amd64.deb; apt-get -fy install

# Install Playwright and its dependencies
RUN npx playwright install && npx playwright install-deps
14 changes: 14 additions & 0 deletions bwell-typescript-react/.devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"name": "e2e Test Container",
"dockerFile": "Dockerfile",
"forwardPorts": [3000],
"postCreateCommand": "npm install",
"customizations": {
"vscode": {
"extensions": [
"ms-vscode.vscode-typescript-tslint-plugin",
"dbaeumer.vscode-eslint"
]
}
}
}
2 changes: 2 additions & 0 deletions bwell-typescript-react/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
VITE_DEFAULT_KEY=""
VITE_DEFAULT_OAUTH_CREDS=""
23 changes: 23 additions & 0 deletions bwell-typescript-react/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# b.well TypeScript SDK Sample App

This is a sample TypeScript Web application meant to be used in testing the b.well TypeScript SDK.

It is built with react, vite, and material-ui.

## How to run it

1. `npm i` (if you haven't run it before)
2. `npm run dev`

## How to run run the tests

1. `npm run e2e`

A .env file will need to be present in this directory. See .env.example for the keys that will need to be supported.

It is also recommended that you run tests in a container to ensure consistent execution across environments. If you are using VS Code, follow these steps:

1. Navigate into this folder (bwell-typescript-react)
2. Click the little green button in the lower-left corner of your screen
3. Select "Reopen in container"
4. You are now running in a Docker container. `npm run e2e` should run the tests, and a report will open in your web browser once the tests finish running.
44 changes: 44 additions & 0 deletions bwell-typescript-react/e2e/HelloPage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { Page } from "@playwright/test";
import config from "./configTypes";

class HelloPage {
private readonly page: Page;

private readonly mainMenuLocator = "#btnMainMenu";
private readonly helloMenuItemLocator = "#helloMenuItem";
private readonly helloButtonLocator = "#btnHello";
private readonly clearButtonLocator = "#btnClear";
private readonly helloResponseLocator = "#preHelloResponse";

constructor(page: Page) {
this.page = page;
}

async navigate() {
await this.page.goto(config.TEST_APP_URL);
await this.clickMainMenu();
await this.clickHelloMenuItem();
}

async clickMainMenu() {
await this.page.click(this.mainMenuLocator);
}

async clickHelloMenuItem() {
await this.page.locator(this.helloMenuItemLocator).click();
}

async clickHelloButton() {
await this.page.click(this.helloButtonLocator);
}

async clickClearButton() {
await this.page.click(this.clearButtonLocator);
}

async getHelloResponse() {
return await this.page.textContent(this.helloResponseLocator);
}
}

export default HelloPage;
49 changes: 49 additions & 0 deletions bwell-typescript-react/e2e/InitializePage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { Page } from "@playwright/test";
import config from "./configTypes";

class InitializePage {
private readonly page: Page;

public readonly mainMenuLocator = "#btnMainMenu";
public readonly initializeMenuItemLocator = "#initializeMenuItem";
public readonly txtKeyLocator = "#txtKey";
public readonly btnInitializeLocator = "#btnInitialize";
public readonly txtOauthCredsLocator = "#txtOauthCreds";
public readonly btnLoginLocator = "#btnLogin";

constructor(page: Page) {
this.page = page;
}

async navigate() {
await this.page.goto(config.TEST_APP_URL);
await this.clickMainMenu();
await this.clickInitializeMenuItem();
}

async clickMainMenu() {
await this.page.click(this.mainMenuLocator);
}

async clickInitializeMenuItem() {
await this.page.locator(this.initializeMenuItemLocator).click();
}

async clickInitializeButton() {
await this.page.click(this.btnInitializeLocator);
}

async enterClientKey(key: string) {
await this.page.fill(this.txtKeyLocator, key);
}

async enterOAuthCreds(creds: string) {
await this.page.fill(this.txtOauthCredsLocator, creds);
}

async clickLoginButton() {
await this.page.click(this.btnLoginLocator);
}
}

export default InitializePage;
50 changes: 50 additions & 0 deletions bwell-typescript-react/e2e/ShadowPage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { Page } from 'playwright';
import config from "./configTypes";

export class ShadowPageModel {
private page: Page;

private readonly mainMenuLocator = "#btnMainMenu";
private readonly shadowMenuItemLocator = "#shadowMenuItem";
private readonly cpShadowLocator = "#cpShadow";
private readonly btnStartSpinnerLocator = "#btnStartSpinner";
private readonly shadowOuterTextSpanLocator = "#spanOuterText";
private readonly shadowInnerTextSpanLocator = "#spanInnerText";

// Initialize the page object
constructor(page: Page) {
this.page = page;
}

async navigate() {
await this.page.goto(config.TEST_APP_URL);
await this.clickMainMenu();
await this.clickShadowMenuItem();
}

async clickMainMenu() {
await this.page.click(this.mainMenuLocator);
}

async clickShadowMenuItem() {
await this.page.locator(this.shadowMenuItemLocator).click();
}

async getOuterDivText() {
return await this.page.locator(this.shadowOuterTextSpanLocator).innerText();
}

async getInnerDivText() {
return await this.page.locator(this.shadowInnerTextSpanLocator).innerText();
}

async startSpinner() {
await this.page.click(this.btnStartSpinnerLocator);
}

async isSpinnerVisible() {
return await this.page.locator(this.cpShadowLocator).isVisible();
}
}

export default ShadowPageModel;
13 changes: 13 additions & 0 deletions bwell-typescript-react/e2e/configTypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Browser, Page } from "playwright";

declare global {
const page: Page;
const browser: Browser;
const browserName: string;
}

const config = {
TEST_APP_URL: "http://localhost:5173",
}

export default config;
21 changes: 21 additions & 0 deletions bwell-typescript-react/e2e/hello.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import HelloPage from "./HelloPage";
import { test, expect } from "@playwright/test";

test("Hello World", async ({ page }) => {
const helloPage = new HelloPage(page);
await helloPage.navigate();

//verify that the response is empty
const helloResponseEmpty = await helloPage.getHelloResponse();
expect(helloResponseEmpty).toBe("");

//click the hello button, check that the box populates as expected
await helloPage.clickHelloButton();
const helloResponsePopulated = await helloPage.getHelloResponse();
expect(helloResponsePopulated).toBe("World!");

//test the clear button as long as we're in here
await helloPage.clickClearButton();
const secondEmptyHelloResponse = await helloPage.getHelloResponse();
expect(secondEmptyHelloResponse).toBe("");
});
62 changes: 62 additions & 0 deletions bwell-typescript-react/e2e/initialize.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import InitializePage from "./InitializePage";
import { test, expect } from "@playwright/test";
import { config } from 'dotenv';

//initialize dotenv so we can access the environment variables
config();

const DEFAULT_KEY = process.env.VITE_DEFAULT_KEY ?? "";
const DEFAULT_OAUTH_CREDS = process.env.VITE_DEFAULT_OAUTH_CREDS ?? "";

test("Navigating to the Initialize page works", async ({ page }) => {
//arrange
const initializePage = new InitializePage(page);

//act
await initializePage.navigate();

//assert: page title should be "b.well Typescript SDK Example Web App"
expect(await page.title()).toBe("b.well Typescript SDK Example Web App");
});

test("Initialize page doesn't display OAuth Creds initially", async ({ page }) => {
//arrange
const initializePage = new InitializePage(page);

//act
await initializePage.navigate();

//assert: oauth creds textarea should not be visible
expect(await page.isVisible(initializePage.txtOauthCredsLocator)).toBe(false);
});

test("Initialize page displays OAuth Creds after client key is entered", async ({ page }) => {
//arrange
const initializePage = new InitializePage(page);

//act
await initializePage.navigate();
await initializePage.enterClientKey(DEFAULT_KEY);
await initializePage.clickInitializeButton();

const oauthCredsLocator = await page.waitForSelector(initializePage.txtOauthCredsLocator);
const oauthCredsVisible = await oauthCredsLocator.isVisible();

//assert: oauth creds textarea should be visible
expect(oauthCredsVisible).toBe(true);
});

test("Entering a valid oauth credential results in success", async ({ page }) => {
//arrange
const initializePage = new InitializePage(page);

//act
await initializePage.navigate();
await initializePage.enterClientKey(DEFAULT_KEY);
await initializePage.clickInitializeButton();

const oauthCredsLocator = await page.waitForSelector(initializePage.txtOauthCredsLocator);
await oauthCredsLocator.fill(DEFAULT_OAUTH_CREDS);

await initializePage.clickLoginButton();
});
29 changes: 29 additions & 0 deletions bwell-typescript-react/e2e/shadow.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import ShadowPage from "./ShadowPage";
import { test, expect } from "@playwright/test";

test("Spinner starts on page load", async ({ page }) => {
const shadowPage = new ShadowPage(page);

await shadowPage.navigate();

expect(await shadowPage.isSpinnerVisible()).toBe(true);
});

test("Spinner stops after 3 seconds", async ({ page }) => {
const shadowPage = new ShadowPage(page);

await shadowPage.navigate();

expect(await shadowPage.isSpinnerVisible()).toBe(true);
await page.waitForTimeout(3000);
expect(await shadowPage.isSpinnerVisible()).toBe(false);
});

test("When spinner stops, the shadow component is visible", async ({ page }) => {
const shadowPage = new ShadowPage(page);

await shadowPage.navigate();

expect(await shadowPage.getInnerDivText()).toBe("This is the inner div in the Shadow DOM");
expect(await shadowPage.getOuterDivText()).toBe("This is the outer div in the Shadow DOM");
});
13 changes: 13 additions & 0 deletions bwell-typescript-react/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>b.well Typescript SDK Example Web App</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
Loading

0 comments on commit 8284de0

Please sign in to comment.