Skip to content
This repository was archived by the owner on Mar 4, 2025. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 3 additions & 38 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,48 +41,13 @@ Alternatively, you can install the prerequisites by following the instruction be
volta install yarn
```

## Getting Started with local development

1. Install Java Runtime Environment (JRE) version 11.x or newer
1. Install nest a global dependency for using the Nest CLI:
```sh
yarn add global nest
```
1. Install application dependencies:
```sh
yarn install
```
1. Update the variables `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, and `AWS_SESSION_TOKEN` under file `apps/core/.env` with your development AWS credentials.
1. Start development server:
```sh
yarn dev
```
1. Application is available at URL: `http://localhost:3000`
1. Log in with local Cognito credentials found at `apps/core/.cognito/db/us-west-2_h23TJjQR9.json`

## Running the tests locally

Run local test command `yarn test` to test the application. The command is "batteries included" - it has everything needed to run and test the application locally.

## Updating generated types locally

Run `yarn gen:types` in root while `yarn dev` is running.

## Deploying to AWS Cloud

Instructions are located [here](https://github.com/awslabs/iot-application/blob/main/deploymentguide/README.md)

## Environments

### Service Dependencies
Instructions are located [here](./deploymentguide/README.md)

The table below lists the service dependencies for different environments.
## Development

| Category\Environments | [Local Development](#getting-started-with-local-development) | [Local Test](#running-the-tests-locally) |
| --------------------- | -------------------------------------------------------------- | --------------------------------------------------------------------- |
| Authentication | [cognito-local](https://www.npmjs.com/package/cognito-local) | [cognito-local](https://www.npmjs.com/package/cognito-local) |
| App API Database | [dynamodb-local](https://www.npmjs.com/package/dynamodb-local) | [dynamodb-local](https://www.npmjs.com/package/dynamodb-local) |
| App API Authorization | [cognito-local](https://www.npmjs.com/package/cognito-local) | [JWT generated from secret](./apps/core/src/testing/jwt-generator.ts) |
To start development, please refer to [development guide](./developmentguide/README.md).

## Security

Expand Down
10 changes: 5 additions & 5 deletions apps/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@
"@cloudscape-design/components": "^3.0.518",
"@cloudscape-design/design-tokens": "3.0.34",
"@cloudscape-design/global-styles": "^1.0.23",
"@iot-app-kit/core": "10.7.0",
"@iot-app-kit/dashboard": "10.7.0",
"@tanstack/react-query": "^5.24.1",
"@iot-app-kit/core": "10.8.0",
"@iot-app-kit/dashboard": "10.8.0",
"@tanstack/react-query": "^5.48.0",
"aws-amplify": "^5.3.11",
"axios": "^1.6.7",
"cytoscape": "^3.28.1",
Expand All @@ -58,8 +58,8 @@
"@formatjs/ts-transformer": "^3.13.9",
"@hookform/devtools": "^4.3.1",
"@originjs/vite-plugin-federation": "^1.3.3",
"@tanstack/eslint-plugin-query": "^5.20.1",
"@tanstack/react-query-devtools": "^5.24.1",
"@tanstack/eslint-plugin-query": "^5.47.0",
"@tanstack/react-query-devtools": "^5.48.0",
"@testing-library/jest-dom": "^6.4.2",
"@testing-library/react": "^14.1.0",
"@testing-library/react-hooks": "^8.0.1",
Expand Down
40 changes: 40 additions & 0 deletions developmentguide/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# IoT Dashboard Application Development Guide

## Getting Started with local development

1. Install Java Runtime Environment (JRE) version 11.x or newer
1. Install nest a global dependency for using the Nest CLI:
```sh
yarn add global nest
```
1. Install application dependencies:
```sh
yarn install
```
1. Update the variables `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, and `AWS_SESSION_TOKEN` under file `apps/core/.env` with your development AWS credentials.
1. Start development server:
```sh
yarn dev
```
1. Application is available at URL: `http://localhost:3000`
1. Log in with local Cognito credentials found at `apps/core/.cognito/db/us-west-2_h23TJjQR9.json`

## Running the tests locally

Run local test command `yarn test` to test the application. The command is "batteries included" - it has everything needed to run and test the application locally.

## Updating generated types locally

Run `yarn gen:types` in root while `yarn dev` is running.

## Environments

### Service Dependencies

The table below lists the service dependencies for different environments.

| Category\Environments | [Local Development](#getting-started-with-local-development) | [Local Test](#running-the-tests-locally) |
| --------------------- | -------------------------------------------------------------- | --------------------------------------------------------------------- |
| Authentication | [cognito-local](https://www.npmjs.com/package/cognito-local) | [cognito-local](https://www.npmjs.com/package/cognito-local) |
| App API Database | [dynamodb-local](https://www.npmjs.com/package/dynamodb-local) | [dynamodb-local](https://www.npmjs.com/package/dynamodb-local) |
| App API Authorization | [cognito-local](https://www.npmjs.com/package/cognito-local) | [JWT generated from secret](./apps/core/src/testing/jwt-generator.ts) |
170 changes: 86 additions & 84 deletions tests/dashboard-list-page.spec.ts
Original file line number Diff line number Diff line change
@@ -1,99 +1,101 @@
import { test, expect, violationFingerprints } from './helpers';

test.describe('dashboard list page', () => {
test('empty page', async ({ page, dashboardListPage }) => {
const noDashboardsLocator = page.getByText('No dashboards', {
exact: true,
});
await noDashboardsLocator.scrollIntoViewIfNeeded();
await expect(noDashboardsLocator).toBeVisible();
await expect(
page.getByText('No dashboards to display', { exact: true }),
).toBeVisible();
await expect(dashboardListPage.emptyCreateButton).toBeVisible();
test('empty page', async ({ page, emptyDashboardListPage }) => {
const noDashboardsLocator = page.getByText('No dashboards', {
exact: true,
});
await page.mouse.wheel(0, 300);
await expect(noDashboardsLocator).toBeVisible();
await expect(
page.getByText('No dashboards to display', { exact: true }),
).toBeVisible();
await expect(emptyDashboardListPage.emptyCreateButton).toBeVisible();
});

test('dashboards are visible', async ({
page,
dashboardListPageWithDashboards: { dashboard1, dashboard2 },
}) => {
await expect(page.getByText(dashboard1.name)).toBeVisible();
await expect(page.getByText(dashboard1.description)).toBeVisible();
await expect(page.getByText(dashboard2.name)).toBeVisible();
await expect(page.getByText(dashboard2.description)).toBeVisible();
});
test('dashboards are visible', async ({
page,
dashboardListPageWithDashboards: { dashboard1, dashboard2 },
}) => {
await page.mouse.wheel(0, 300);
await expect(page.getByText(dashboard1.name)).toBeVisible();
await expect(page.getByText(dashboard1.description)).toBeVisible();
await expect(page.getByText(dashboard2.name)).toBeVisible();
await expect(page.getByText(dashboard2.description)).toBeVisible();
});

test('a single dashboard can be deleted', async ({
page,
applicationFrame,
dashboardListPageWithDashboards: {
dashboardListPage,
dashboard1,
dashboard2,
},
}) => {
await page
.getByRole('checkbox', { name: `Select dashboard ${dashboard2.name}` })
.click();
await dashboardListPage.deleteButton.click();
await dashboardListPage.deleteDashboardDialog.deleteButton.click();
test('a single dashboard can be deleted', async ({
page,
applicationFrame,
dashboardListPageWithDashboards: {
dashboardListPage,
dashboard1,
dashboard2,
},
}) => {
await page.mouse.wheel(0, 300);
await page
.getByRole('checkbox', { name: `Select dashboard ${dashboard2.name}` })
.click();
await dashboardListPage.deleteButton.click();
await dashboardListPage.deleteDashboardDialog.deleteButton.click();

await expect(applicationFrame.notification).toBeVisible();
await applicationFrame.dismissNotificationButton.click();
await expect(applicationFrame.notification).toBeVisible();
await applicationFrame.dismissNotificationButton.click();

await expect(page.getByText(dashboard1.name)).toBeVisible();
await expect(page.getByText(dashboard1.description)).toBeVisible();
await expect(page.getByText(dashboard2.name)).toBeHidden();
await expect(page.getByText(dashboard2.description)).toBeHidden();
});
await expect(page.getByText(dashboard1.name)).toBeVisible();
await expect(page.getByText(dashboard1.description)).toBeVisible();
await expect(page.getByText(dashboard2.name)).toBeHidden();
await expect(page.getByText(dashboard2.description)).toBeHidden();
});

test('multiple dashboards can be deleted', async ({
page,
applicationFrame,
dashboardListPageWithDashboards: {
dashboardListPage,
dashboard1,
dashboard2,
},
}) => {
await page
.getByRole('checkbox', { name: `Select dashboard ${dashboard1.name}` })
.click();
await page
.getByRole('checkbox', { name: `Select dashboard ${dashboard2.name}` })
.click();
await dashboardListPage.deleteButton.click();
await dashboardListPage.deleteDashboardDialog.deleteButton.click();
test('multiple dashboards can be deleted', async ({
page,
applicationFrame,
dashboardListPageWithDashboards: {
dashboardListPage,
dashboard1,
dashboard2,
},
}) => {
await page.mouse.wheel(0, 300);
await page
.getByRole('checkbox', { name: `Select dashboard ${dashboard1.name}` })
.click();
await page
.getByRole('checkbox', { name: `Select dashboard ${dashboard2.name}` })
.click();
await dashboardListPage.deleteButton.click();
await dashboardListPage.deleteDashboardDialog.deleteButton.click();

await expect(applicationFrame.notification).toBeVisible();
await applicationFrame.dismissNotificationButton.click();
await expect(applicationFrame.notification).toBeVisible();
await applicationFrame.dismissNotificationButton.click();

const emptyButton = dashboardListPage.emptyCreateButton;
const emptyButton = dashboardListPage.emptyCreateButton;

await emptyButton.scrollIntoViewIfNeeded();
await expect(emptyButton).toBeVisible();
await expect(page.getByText(dashboard1.name)).toBeHidden();
await expect(page.getByText(dashboard1.description)).toBeHidden();
await expect(page.getByText(dashboard2.name)).toBeHidden();
await expect(page.getByText(dashboard2.description)).toBeHidden();
});
await page.mouse.wheel(0, 200);
await expect(emptyButton).toBeVisible();
await expect(page.getByText(dashboard1.name)).toBeHidden();
await expect(page.getByText(dashboard1.description)).toBeHidden();
await expect(page.getByText(dashboard2.name)).toBeHidden();
await expect(page.getByText(dashboard2.description)).toBeHidden();
});

test('accessibility', async ({
makeAxeBuilder,
page,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
dashboardListPageWithDashboards: { dashboard1, dashboard2 },
}) => {
await expect(page.getByText(dashboard1.name)).toBeVisible();
await expect(page.getByText(dashboard1.description)).toBeVisible();
await expect(page.getByText(dashboard2.name)).toBeVisible();
await expect(page.getByText(dashboard2.description)).toBeVisible();
test('accessibility', async ({
makeAxeBuilder,
page,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
dashboardListPageWithDashboards: { dashboard1, dashboard2 },
}) => {
await page.mouse.wheel(0, 300);
await expect(page.getByText(dashboard1.name)).toBeVisible();
await expect(page.getByText(dashboard1.description)).toBeVisible();
await expect(page.getByText(dashboard2.name)).toBeVisible();
await expect(page.getByText(dashboard2.description)).toBeVisible();

const accessibilityScanResults = await makeAxeBuilder().analyze();
const accessibilityScanResults = await makeAxeBuilder().analyze();

// TODO: fix target-size violation
expect(violationFingerprints(accessibilityScanResults)).toMatchSnapshot(
'dashboard-list-page-accessibility-scan-results',
);
});
// TODO: fix target-size violation
expect(violationFingerprints(accessibilityScanResults)).toMatchSnapshot(
'dashboard-list-page-accessibility-scan-results',
);
});
15 changes: 6 additions & 9 deletions tests/dashboards/dashboard-management.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,6 @@ test('as a user, I can create, update, and delete my dashboard', async ({
violationFingerprints(dashboardPageAccessibilityScanResults),
).toMatchSnapshot('dashboard-page-accessibility-scan-results');

// TODO: Need to clean up the below, we do not persist anymore
//check if dashboard setting persists
await page.getByRole('button', { name: 'Settings' }).nth(1).click();
await expect(page.getByLabel('Number of Rows')).toHaveValue('1000');
await expect(page.getByLabel('Number of Columns')).toHaveValue('200');
Expand All @@ -63,8 +61,7 @@ test('as a user, I can create, update, and delete my dashboard', async ({
await page.getByLabel('Number of Rows').fill('100');
await page.getByLabel('Number of Columns').fill('100');
await page.getByLabel('cell Size').fill('10');

await page.getByRole('button', { name: 'Close' }).click();
await page.getByRole('button', { name: 'Apply changes' }).click();
await page.getByRole('button', { name: 'Save' }).click();
await page.reload();
await page.getByRole('button', { name: 'Edit' }).click();
Expand All @@ -79,9 +76,9 @@ test('as a user, I can create, update, and delete my dashboard', async ({
// check if viewport setting persists
await page.getByRole('button', { name: 'Preview' }).click();

await page.getByRole('button', { name: 'Last 10 minutes' }).click();
await page.getByRole('button', { name: 'Last 5 minutes' }).click();

await page.getByRole('radio', { name: 'Last 10 minutes' }).click();
await page.getByRole('radio', { name: 'Last 5 minutes' }).click();
await page.getByRole('button', { name: 'Apply' }).click();

await page.getByRole('button', { name: 'Save' }).click();
Expand All @@ -92,9 +89,9 @@ test('as a user, I can create, update, and delete my dashboard', async ({
);

await page.reload();
await expect(
page.getByRole('button', { name: 'Last 10 minutes' }),
).toHaveText('Last 10 minutes');
await expect(page.getByRole('button', { name: 'Last 5 minutes' })).toHaveText(
'Last 5 minutes',
);

await dashboardsPage.goto();

Expand Down
10 changes: 5 additions & 5 deletions tests/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ interface Fixtures {
dashboardPage: DashboardPage;
dashboard: Dashboard;
};
dashboardListPage: DashboardsIndexPage;
emptyDashboardListPage: DashboardsIndexPage;
dashboardListPageWithDashboards: {
dashboardListPage: DashboardsIndexPage;
dashboard1: Dashboard;
Expand Down Expand Up @@ -106,12 +106,12 @@ export const test = base.extend<Fixtures>({

await deleteDashboards({ ids: [dashboard.id] });
},
dashboardListPage: async ({ page }, use) => {
const dashboardListPage = new DashboardsIndexPage(page);
emptyDashboardListPage: async ({ page }, use) => {
const emptyDashboardListPage = new DashboardsIndexPage(page);

await dashboardListPage.goto();
await emptyDashboardListPage.goto();

await use(dashboardListPage);
await use(emptyDashboardListPage);
},
dashboardListPageWithDashboards: async ({ page }, use) => {
const dashboardListPage = new DashboardsIndexPage(page);
Expand Down
Loading