Skip to content

Commit

Permalink
(chore) Dependency upgrades and improvements to linting and types (#190)
Browse files Browse the repository at this point in the history
  • Loading branch information
denniskigen authored Sep 20, 2023
1 parent 14d3050 commit cd4e306
Show file tree
Hide file tree
Showing 78 changed files with 4,875 additions and 3,534 deletions.
5 changes: 4 additions & 1 deletion .eslintignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
**/dist/*
**/node_modules/*
**/*.d.tsx
src/**/*.test.tsx
**/*.d.tsx
__mocks__/*
44 changes: 38 additions & 6 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -1,20 +1,51 @@
{
"parser": "@typescript-eslint/parser",
"plugins": [
"@typescript-eslint"
],
"root": true,
"env": {
"node": true
},
"extends": [
"eslint:recommended",
"plugin:playwright/recommended",
"plugin:prettier/recommended",
"plugin:@typescript-eslint/recommended",
"ts-react-important-stuff"
"plugin:@typescript-eslint/recommended-requiring-type-checking",
"prettier"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": true,
"tsconfigRootDir": "__dirname"
},
"plugins": ["@typescript-eslint"],
"root": true,
"rules": {
// The following rules need `noImplicitAny` to be set to `true` in our tsconfig. They are too restrictive for now, but should be reconsidered in future
"@typescript-eslint/no-unsafe-assignment": "off",
"@typescript-eslint/no-unsafe-call": "off",
"@typescript-eslint/no-unsafe-member-access": "off",
"@typescript-eslint/unbound-method": "off",
// Nitpicky. Prefer `interface T` over type T
"@typescript-eslint/consistent-type-definitions": "error",
"@typescript-eslint/consistent-type-exports": "error",
// Use `import type` instead of `import` for type imports
"@typescript-eslint/consistent-type-imports": [
"error",
{
"prefer": "type-imports",
"disallowTypeAnnotations": true
}
],
// Use Array<T> instead of T[] consistently
"@typescript-eslint/array-type": [
"error",
{
"default": "generic"
}
],
"no-restricted-imports": [
"error",
{
"paths": [
// These two rules ensure that we're importing lodash and lodash-es correctly. Not doing so can bloat our bundle size significantly.
{
"name": "lodash",
"message": "Import specific methods from `lodash`. e.g. `import map from 'lodash/map'`"
Expand All @@ -24,6 +55,7 @@
"importNames": ["default"],
"message": "Import specific methods from `lodash-es`. e.g. `import { map } from 'lodash-es'`"
},
// These two rules ensure that we're importing Carbon components and icons from the correct packages (after v10). May be removed in the future.
{
"name": "carbon-components-react",
"message": "Import from `@carbon/react` directly. e.g. `import { Toggle } from '@carbon/react'`"
Expand Down
3 changes: 2 additions & 1 deletion .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

yarn pretty-quick --staged
yarn prettier && npx lint-staged
yarn turbo extract-translations
8 changes: 8 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"bracketSpacing": true,
"printWidth": 120,
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "all"
}
60 changes: 43 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,20 @@ The Form Builder is a widget used to create OpenMRS form schemas. It enables use

## Form Builder User Guide

* See the thorough User Guide for the Form Builder here: https://ampath-forms.vercel.app/docs/quickstart
* Prerequisites & dependencies are covered here: https://ampath-forms.vercel.app/docs/developer-guide/run-form-engine-in-openmrs3#prerequisites
* See the thorough User Guide for the Form Builder here: <https://ampath-forms.vercel.app/docs/quickstart>
* Prerequisites & dependencies are covered here: <https://ampath-forms.vercel.app/docs/developer-guide/run-form-engine-in-openmrs3#prerequisites>

## Running this code

Under the hood, the Form Builder uses the [OHRI form engine](https://www.npmjs.com/package/@openmrs/openmrs-form-engine-lib) to render a visual representation of your schema. This visual preview gets progressively updated as you build your schema. When done building, you can save your schema to an OpenMRS server. You can also publish your schema to make it available to your frontend.

To set up environment variables for the project, follow these steps:

1. Create a copy of the .env.example file by running the following command:

```bash
cp example.env .env
```
```bash
cp example.env .env
```

2. Open the newly created .env file in the root of the project.

Expand All @@ -35,17 +36,49 @@ yarn start # Launches a dev server

Once the dev server launches, log in and select a location. You will get redirected to the home page. Once there, you can either:

- Click the App Switcher icon in the top right corner and then click `Form Builder` to launch the app.
- Manually navigate to the `/openmrs/spa/form-builder` URL.
* Click the App Switcher icon in the top right corner and then click the `System Administration` link to go the Admin page. Click on the `Form Builder` tile to launch the app.
* Manually navigate to the `/openmrs/spa/form-builder` URL.

## Running tests

### Unit tests

To run unit tests, use:
To run tests for all packages, run:

```sh
yarn test
```bash
yarn turbo test
```

To run tests in `watch` mode, run:

```bash
yarn turbo test:watch
```

To run a specific test file, run:

```bash
yarn turbo test -- dashboard
```

The above command will only run tests in the file or files that match the provided string.

You can also run the matching tests from above in watch mode by running:

```bash
yarn turbo test:watch -- dashboard
```

To generate a `coverage` report, run:

```bash
yarn turbo coverage
```

By default, `turbo` will cache test runs. This means that re-running tests wihout changing any of the related files will return the cached logs from the last run. To bypass the cache, run tests with the `force` flag, as follows:

```bash
yarn turbo test --force
```

### E2E tests
Expand All @@ -64,15 +97,8 @@ yarn test-e2e --headed

Please read [our e2e docs](e2e/README.md) for more information about E2E testing.


## Building

```sh
yarn build
```

## Running tests

```sh
yarn test
```
23 changes: 9 additions & 14 deletions __mocks__/react-i18next.js
Original file line number Diff line number Diff line change
@@ -1,43 +1,38 @@
/** At present, this entire mock is boilerplate. */

const React = require("react");
const reactI18next = require("react-i18next");
const React = require('react');
const reactI18next = require('react-i18next');

const hasChildren = (node) =>
node && (node.children || (node.props && node.props.children));
const hasChildren = (node) => node && (node.children || (node.props && node.props.children));

const getChildren = (node) =>
node && node.children ? node.children : node.props && node.props.children;
const getChildren = (node) => (node && node.children ? node.children : node.props && node.props.children);

const renderNodes = (reactNodes) => {
if (typeof reactNodes === "string") {
if (typeof reactNodes === 'string') {
return reactNodes;
}

return Object.keys(reactNodes).map((key, i) => {
const child = reactNodes[key];
const isElement = React.isValidElement(child);

if (typeof child === "string") {
if (typeof child === 'string') {
return child;
}
if (hasChildren(child)) {
const inner = renderNodes(getChildren(child));
return React.cloneElement(child, { ...child.props, key: i }, inner);
}
if (typeof child === "object" && !isElement) {
return Object.keys(child).reduce(
(str, childKey) => `${str}${child[childKey]}`,
""
);
if (typeof child === 'object' && !isElement) {
return Object.keys(child).reduce((str, childKey) => `${str}${child[childKey]}`, '');
}

return child;
});
};

const useMock = [(k) => k, {}];
useMock.t = (k, o) => (o && o.defaultValue) || (typeof o === "string" ? o : k);
useMock.t = (k, o) => (o && o.defaultValue) || (typeof o === 'string' ? o : k);
useMock.i18n = {};

module.exports = {
Expand Down
52 changes: 23 additions & 29 deletions e2e/commands/form-operations.ts
Original file line number Diff line number Diff line change
@@ -1,66 +1,60 @@
import { APIRequestContext, expect } from "@playwright/test";
import customSchema from "../support/custom-schema.json";
import type { APIRequestContext } from '@playwright/test';
import { expect } from '@playwright/test';
import customSchema from '../support/custom-schema.json';
import type { Form } from '../../src/types';

export const createForm = async (
api: APIRequestContext,
isFormPublished: boolean
) => {
const formResponse = await api.post("form", {
export const createForm = async (api: APIRequestContext, isFormPublished: boolean) => {
const formResponse = await api.post('form', {
data: {
name: `A sample test form ${Math.floor(Math.random() * 10000)}`,
version: "1.0",
version: '1.0',
published: isFormPublished,
description: "This is the form description",
description: 'This is the form description',
encounterType: {
uuid: "e22e39fd-7db2-45e7-80f1-60fa0d5a4378",
uuid: 'e22e39fd-7db2-45e7-80f1-60fa0d5a4378',
},
},
});
await expect(formResponse.ok()).toBeTruthy();
expect(formResponse.ok()).toBeTruthy();
const form = await formResponse.json();

return form;
return form as Form;
};

export const addFormResources = async (
api: APIRequestContext,
valueReference: string,
formUuid: string
) => {
export const addFormResources = async (api: APIRequestContext, valueReference: string, formUuid: string) => {
const formResourcesRes = await api.post(`form/${formUuid}/resource`, {
data: {
name: "JSON schema",
dataType: "AmpathJsonSchema",
name: 'JSON schema',
dataType: 'AmpathJsonSchema',
valueReference: valueReference,
},
});
await expect(formResourcesRes.ok()).toBeTruthy();
expect(formResourcesRes.ok()).toBeTruthy();
};

export const createValueReference = async (api: APIRequestContext) => {
const boundary =
"--------------------------" + Math.floor(Math.random() * 1e16);
const delimiter = "\r\n--" + boundary + "\r\n";
const closeDelimiter = "\r\n--" + boundary + "--";
const boundary = '--------------------------' + Math.floor(Math.random() * 1e16);
const delimiter = '\r\n--' + boundary + '\r\n';
const closeDelimiter = '\r\n--' + boundary + '--';

const body =
delimiter +
'Content-Disposition: form-data; name="file"; filename="schema.json"\r\n' +
"Content-Type: application/json\r\n\r\n" +
'Content-Type: application/json\r\n\r\n' +
JSON.stringify(customSchema) +
closeDelimiter;

const valueReference = await api.post("clobdata", {
const valueReference = await api.post('clobdata', {
data: body,
headers: {
"Content-Type": "multipart/form-data; boundary=" + boundary,
'Content-Type': 'multipart/form-data; boundary=' + boundary,
},
});
await expect(valueReference.ok()).toBeTruthy();
expect(valueReference.ok()).toBeTruthy();
return await valueReference.text();
};

export async function deleteForm(api: APIRequestContext, uuid: string) {
const formDeleteRes = await api.delete(`form/${uuid}`, { data: {} });
await expect(formDeleteRes.ok()).toBeTruthy();
expect(formDeleteRes.ok()).toBeTruthy();
}
16 changes: 8 additions & 8 deletions e2e/core/global-setup.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { request } from "@playwright/test";
import * as dotenv from "dotenv";
import { request } from '@playwright/test';
import * as dotenv from 'dotenv';

dotenv.config();

Expand All @@ -12,20 +12,20 @@ dotenv.config();

async function globalSetup() {
const requestContext = await request.newContext();
const token = Buffer.from(
`${process.env.E2E_USER_ADMIN_USERNAME}:${process.env.E2E_USER_ADMIN_PASSWORD}`
).toString("base64");
const token = Buffer.from(`${process.env.E2E_USER_ADMIN_USERNAME}:${process.env.E2E_USER_ADMIN_PASSWORD}`).toString(
'base64',
);
await requestContext.post(`${process.env.E2E_BASE_URL}/ws/rest/v1/session`, {
data: {
sessionLocation: process.env.E2E_LOGIN_DEFAULT_LOCATION_UUID,
locale: "en",
locale: 'en',
},
headers: {
"Content-Type": "application/json",
'Content-Type': 'application/json',
Authorization: `Basic ${token}`,
},
});
await requestContext.storageState({ path: "e2e/storageState.json" });
await requestContext.storageState({ path: 'e2e/storageState.json' });
await requestContext.dispose();
}

Expand Down
2 changes: 1 addition & 1 deletion e2e/core/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export * from "./test";
export * from './test';
7 changes: 4 additions & 3 deletions e2e/core/test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { APIRequestContext, Page, test as base } from "@playwright/test";
import { api } from "../fixtures";
import type { APIRequestContext, Page } from '@playwright/test';
import { test as base } from '@playwright/test';
import { api } from '../fixtures';

// This file sets up our custom test harness using the custom fixtures.
// See https://playwright.dev/docs/test-fixtures#creating-a-fixture for details.
Expand All @@ -16,5 +17,5 @@ export interface CustomWorkerFixtures {
}

export const test = base.extend<CustomTestFixtures, CustomWorkerFixtures>({
api: [api, { scope: "worker" }],
api: [api, { scope: 'worker' }],
});
Loading

0 comments on commit cd4e306

Please sign in to comment.