Skip to content

Commit

Permalink
Component library unit tests (#34)
Browse files Browse the repository at this point in the history
* Move crud operations into "form-manager", along with DocumentImporter.

* Rename "stories" to "example-stories"

* PascalCase for FormManager

* Create top-level Form design component.

* Rename forms/Form type to forms/FormDefinition

* Add Storybook a11y plugin, which has Axe integrated.

* Add play function to Form story and initial unit test.

* Add tests for each Storybook stories module, using a new helper - describeStories - to compose storybook stories and their "play" functions into Vitest specs. Integrate so it's included with the workspace's coverage metrics. (there's a failing test that will be fixed in a follow-up commit.)

* Get design unit tests running via the workspace's Vitest configuration. This required added a "jsdom" environment annotation to every unit test file, despite there being a global jsdom environment configuration setting in the project. (running just the design test suite from the packages/design directory picks up the environment correctly; not so when the entire monorepo is tested at the same time).

* Update README, moving pdf->html parsing notes into comment on processHtml.js. Also, add linting to CI.

* Move FormManager implementation into the package's index.

* To reduce memory consumption on Cloud.gov Pages, reduce Turborepo's build concurrency from the default of 10 to 5.

* Log available memory in the Pages build container.

* Try building on Pages with a Turborepo concurrency of 1 (for serial build execution).

* Remove single concurrency from Turborepo build
  • Loading branch information
danielnaab authored Jan 26, 2024
1 parent 3f124ed commit c0997fd
Show file tree
Hide file tree
Showing 81 changed files with 2,447 additions and 1,175 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ jobs:
- name: Install dependencies
run: pnpm install

- name: Lint source code
shell: bash
run: pnpm lint

- name: Run test suite
shell: bash
run: pnpm test
Expand Down
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v18.16.0
v20.11.0
35 changes: 10 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,50 +21,35 @@ This project uses [pnpm workspaces](https://pnpm.io/workspaces). To work with th
pnpm install
```

Run the test suite with coverage metrics generated:
To run the complete test suite, with coverage metrics generated:

```bash
pnpm test
```

To develop on the frontend app, run:
To run tests in watch mode (except the `infra` tests, which use Jest):

```bash
pnpm build
pnpm vitest
```

then
To start developing with hot reloading, use:

```bash
cd apps/spotlight
pnpm dev --concurrency 12
```

To run an app server:
These local servers will be started:

```bash
pnpm start
```

To extract form fields, their attributes and labels from an HTML code and output them in a JSON file, go to the "htmlParser" directory:

```bash
cd apps/spotlight/src/htmlParser
```
- Astro website - http://localhost:4321/
- Storybook - http://localhost:61610/

If you're already in the "spotlight" directory
To lint the source code:

```bash
cd src/htmlParser
pnpm lint
```

Replace the HTML content that's inside the "form-input.html" with your HTML, then run:

```bash
node processHtml.js yourCustomJSONFileName.json
```

Be sure to replace "yourCustomJSONFileName.json" with whatever name you want your output JSON file to be called. If you don't indicate a new file name, your file will be given the default file name which is "form-field-output.json".

## Command-line interface

A command-line interface is provided for manually running operations. The corresponding app resides in [./apps/cli](./apps/cli). A wrapper script, in the root directory, is provided.
Expand Down
64 changes: 43 additions & 21 deletions apps/spotlight/src/htmlParser/processHtml.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,24 @@
/**
To extract form fields, their attributes and labels from an HTML code and output them in a JSON file, go to the "htmlParser" directory:
```bash
cd apps/spotlight/src/htmlParser
```
If you're already in the "spotlight" directory
```bash
cd src/htmlParser
```
Replace the HTML content that's inside the "form-input.html" with your HTML, then run:
```bash
node processHtml.js yourCustomJSONFileName.json
```
Be sure to replace "yourCustomJSONFileName.json" with whatever name you want your output JSON file to be called. If you don't indicate a new file name, your file will be given the default file name which is "form-field-output.json".
*/
import { load } from 'cheerio';
import fs from 'fs';
import path from 'path';
Expand All @@ -8,32 +29,33 @@ const outputFileName = process.argv[2] || defaultOutputFileName;
const outputJsonPath = path.join(process.cwd(), outputFileName);

function extractFormFields(htmlContent) {
const $ = load(htmlContent);
let formFields = [];

$('input, textarea, select').each((index, element) => {
const field = $(element);
const label = $("label[for='" + field.attr('id') + "']").text() || 'No Label';
formFields.push({
tag: field.prop('tagName').toLowerCase(),
type: field.attr('type'),
name: field.attr('name'),
id: field.attr('id'),
value: field.attr('value'),
label: label.trim()
});
const $ = load(htmlContent);
let formFields = [];

$('input, textarea, select').each((index, element) => {
const field = $(element);
const label =
$("label[for='" + field.attr('id') + "']").text() || 'No Label';

formFields.push({
tag: field.prop('tagName').toLowerCase(),
type: field.attr('type'),
name: field.attr('name'),
id: field.attr('id'),
value: field.attr('value'),
label: label.trim(),
});
});

return formFields;
return formFields;
}

function processHtmlFile() {
const htmlContent = fs.readFileSync(htmlFilePath, 'utf8');
const formFields = extractFormFields(htmlContent);
const htmlContent = fs.readFileSync(htmlFilePath, 'utf8');
const formFields = extractFormFields(htmlContent);

fs.writeFileSync(outputJsonPath, JSON.stringify(formFields, null, 2));
console.log(`Processed HTML file. Form data written to ${outputJsonPath}`);
fs.writeFileSync(outputJsonPath, JSON.stringify(formFields, null, 2));
console.log(`Processed HTML file. Form data written to ${outputJsonPath}`);
}

processHtmlFile();
processHtmlFile();
4 changes: 2 additions & 2 deletions apps/spotlight/src/pages/forms/index.astro
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
---
import { FormSection } from '@atj/design/src/form';
import { FormManager } from '@atj/design';
import ContentLayout from '../../layouts/ContentLayout.astro';
---

<ContentLayout title="10x Access to Justice Spotlight">
<FormSection client:only />
<FormManager client:only />
</ContentLayout>
5 changes: 3 additions & 2 deletions apps/spotlight/src/pages/ud105-evicition-form.astro
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
---
import { FormView } from '@atj/design/src/form/FormView';
import { Form } from '@atj/design';
import formData from '../htmlParser/ud105-form-field-output.json';
import ContentLayout from '../layouts/ContentLayout.astro';
---

<ContentLayout title="10x Access to Justice Spotlight">
<FormView prompt={formData} />
<Form client:only form={formData} />
</ContentLayout>
6 changes: 3 additions & 3 deletions infra/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
"main": "src/index.js",
"types": "src/index.ts",
"scripts": {
"get": "cdktf get",
"compile": "tsc --pretty",
"dev": "tsc -w",
"deploy": "cdktf deploy",
"get": "cdktf get",
"synth": "cdktf synth",
"compile": "tsc --pretty",
"watch": "tsc -w",
"test": "jest",
"test:watch": "jest --watch"
},
Expand Down
7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@
"build": "turbo run build",
"clean": "turbo run clean",
"dev": "turbo run dev",
"format": "prettier --write \"packages/**/*.{js,jsx,ts,tsx}\"",
"lint": "turbo run lint",
"pages": "rm -rf node_modules && npm i -g pnpm turbo && pnpm i && pnpm build && ln -sf ./apps/spotlight/dist _site",
"test": "run-p test:*",
"test:infra": "turbo run --filter=infra test",
"test:vitest": "vitest run --coverage.enabled --coverage.reporter=text --coverage.reporter=json-summary --coverage.reporter=json --coverage.reportOnFailure --reporter vitest-github-actions-reporter",
"format": "prettier --write \"packages/**/*.{js,jsx,ts,tsx}\""
"test:vitest": "vitest run --coverage.enabled --coverage.reporter=text --coverage.reporter=json-summary --coverage.reporter=json --coverage.reportOnFailure --reporter vitest-github-actions-reporter"
},
"hooks": {
"pre-commit": "pnpm format"
Expand All @@ -23,11 +24,13 @@
"@vitest/coverage-c8": "^0.33.0",
"@vitest/coverage-v8": "^0.34.6",
"@vitest/ui": "^1.2.1",
"eslint": "^8.56.0",
"npm-run-all": "^4.1.5",
"prettier": "^3.0.3",
"ts-node": "^10.9.1",
"tsup": "^7.2.0",
"turbo": "^1.10.16",
"type-fest": "^4.10.1",
"typescript": "^5.2.2",
"vitest": "^0.34.6",
"vitest-github-actions-reporter": "^0.11.1",
Expand Down
35 changes: 35 additions & 0 deletions packages/design/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
module.exports = {
"env": {
"browser": true,
"es2021": true
},
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:react/recommended"
],
"overrides": [
{
"env": {
"node": true
},
"files": [
".eslintrc.{js,cjs}"
],
"parserOptions": {
"sourceType": "script"
}
}
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module"
},
"plugins": [
"@typescript-eslint",
"react"
],
"rules": {
}
}
2 changes: 2 additions & 0 deletions packages/design/.storybook/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ const config: StorybookConfig = {
getAbsolutePath('@storybook/addon-essentials'),
getAbsolutePath('@storybook/addon-onboarding'),
getAbsolutePath('@storybook/addon-interactions'),
getAbsolutePath('@storybook/addon-a11y'),
getAbsolutePath('@storybook/addon-coverage'),
],
framework: {
name: getAbsolutePath('@storybook/react-vite') as '@storybook/react-vite',
Expand Down
34 changes: 26 additions & 8 deletions packages/design/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,41 +11,59 @@
"clean": "run-p: clean:*",
"clean:lib": "rm -rf dist",
"clean:styles": "rm -rf static",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"storybook": "storybook dev -p 6006",
"watch": "run-p watch:*",
"watch:lib": "vite",
"watch:storybook": "storybook dev",
"watch:styles": "gulp watch"
"dev": "run-p dev:*",
"dev:lib": "vite",
"dev:storybook": "storybook dev",
"dev:styles": "gulp watch",
"lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"test:storybook": "test-storybook",
"test:url": "test-storybook --url http://127.0.0.1:9009 --config-dir storybook",
"test:ci": "concurrently -k -s first -n \"SB,TEST\" -c \"magenta,blue\" \"pnpm build:storybook --quiet && npx http-server storybook/public --port 9009 --silent\" \"wait-on tcp:127.0.0.1:9009 && pnpm test:url --maxWorkers=2\""
},
"files": [
"dist/**/*"
],
"devDependencies": {
"@playwright/test": "^1.41.1",
"@storybook/addon-a11y": "^7.6.10",
"@storybook/addon-coverage": "^1.0.0",
"@storybook/addon-essentials": "^7.6.10",
"@storybook/addon-interactions": "^7.6.10",
"@storybook/addon-links": "^7.6.10",
"@storybook/addon-onboarding": "^1.0.10",
"@storybook/blocks": "^7.6.10",
"@storybook/preview-api": "^7.6.10",
"@storybook/react": "^7.6.10",
"@storybook/react-vite": "^7.6.10",
"@storybook/test": "^7.6.10",
"@storybook/test-runner": "^0.16.0",
"@storybook/types": "^7.6.10",
"@testing-library/react": "^14.1.2",
"@types/prop-types": "^15.7.11",
"@types/react": "^18.2.48",
"@typescript-eslint/eslint-plugin": "^6.19.1",
"@typescript-eslint/parser": "^6.19.1",
"@uswds/compile": "github:danielnaab/uswds-compile#package-json-paths",
"@vitejs/plugin-react": "^4.2.1",
"concurrently": "^8.2.2",
"eslint": "^8.56.0",
"eslint-plugin-react": "^7.33.2",
"glob": "^10.3.10",
"gulp": "^4.0.2",
"http-server": "^14.1.1",
"install": "^0.13.0",
"npm": "^10.3.0",
"prop-types": "^15.8.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"vite": "^5.0.12",
"vite-plugin-dts": "^3.7.1"
"vite-plugin-dts": "^3.7.1",
"wait-on": "^7.2.0"
},
"dependencies": {
"@atj/documents": "workspace:*",
"@atj/forms": "workspace:*",
"@atj/form-service": "workspace:*",
"@atj/forms": "workspace:*",
"@atj/interviews": "workspace:*",
"@uswds/uswds": "^3.7.1",
"react-hook-form": "^7.49.3",
Expand Down
37 changes: 37 additions & 0 deletions packages/design/src/Form/Form.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React from 'react';
import type { Meta, StoryObj } from '@storybook/react';

import { createForm } from '@atj/forms';

import Form from '.';

export default {
title: 'Form',
component: Form,
decorators: [(Story, args) => <Story {...args} />],
args: {
form: createForm(
{
title: 'Test form',
description: 'Test description',
},
[
{
id: 'question-1',
text: 'Question 1',
initial: '',
required: true,
},
{
id: 'question-2',
text: 'Question 2',
initial: 'initial value',
required: false,
},
]
),
},
tags: ['autodocs'],
} satisfies Meta<typeof Form>;

export const FormTest = {} satisfies StoryObj<typeof Form>;
7 changes: 7 additions & 0 deletions packages/design/src/Form/Form.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/**
* @vitest-environment jsdom
*/
import { describeStories } from '../test-helper';
import meta, * as stories from './Form.stories';

describeStories(meta.title, stories);
Loading

0 comments on commit c0997fd

Please sign in to comment.