Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test bundle, Documentation & Codi 🐶 Update #1707

Open
wants to merge 13 commits into
base: patch
Choose a base branch
from
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ caddy_linux_amd64
.ebextensions/*
\.ebextensions/

public/js/lib/*
public/js/*

*.pem
*.crt
Expand Down
10 changes: 5 additions & 5 deletions DEVELOPING.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Please check the full list of dependencies as defined in the [package.json](http

The MAPP and MAPP.UI library must be build with [esbuild](https://esbuild.github.io/) prior to launching the host.

npx esbuild ./lib/mapp.mjs ./lib/ui.mjs --bundle --minify --tree-shaking=false --sourcemap --format=iife --outdir=./public/js/lib
npx esbuild ./lib/mapp.mjs ./lib/ui.mjs --bundle --minify --tree-shaking=false --sourcemap --format=iife --outdir=./public/js

The build command is stored in the package.json as `_build` script.

Expand Down Expand Up @@ -79,13 +79,13 @@ A task can be added to the `.vscode/tasks.json` to execute `nodemon` and `browse
}
```

The `browser-sync` script is defined in the `package.json` as `"npx browser-sync start --proxy localhost:3000 --port 3001 --ui-port 3002 --files public/js/lib/**/* --no-open --no-notify"`
The `browser-sync` script is defined in the `package.json` as `"npx browser-sync start --proxy localhost:3000 --port 3001 --ui-port 3002 --files public/js/**/* --no-open --no-notify"`

The application running on port 3000 will be proxied to port 3001 for the browser-sync event. The browser window will refresh when the node application rebuilds after changes to the script in a VSCode editor.

#### VSCode / Chrome Debugging
#### VSCode / Chrome Debugging

An additional debug configuration in `.vscode/launch.json` is required to debug the mapp lib code in VSCode.
An additional debug configuration in `.vscode/launch.json` is required to debug the mapp lib code in VSCode.

```json
{
Expand All @@ -103,7 +103,7 @@ The Chrome debug config must be launched as an additional session. VSCode run an

Breakpoints set in the mapp lib script will now be respected in the VSCode debug editor window. Breakpoints set in the Chrome dev tools will also break in the VSCode editor.

The browser will automatically reload on changes to files in the `lib`, 'tests' and `public/css' directories.
The browser will automatically reload on changes to files in the `lib`, 'tests' and `public/css' directories.

#### Additional settings for VSCode

Expand Down
198 changes: 152 additions & 46 deletions TESTING.md
Original file line number Diff line number Diff line change
@@ -1,78 +1,184 @@
# Testing

Testing in xyz is split up into 3 different sections.
Testing in xyz is split into 3 different sections:

1. cli (console)
2. module (browser)
3. integrity

## cli (console)
## Testing Environment Setup

The cli tests are normal vanilla javascript tests that execute with the nodejs runtime using the [Codi Test framework](https://www.npmjs.com/package/codi-test-framework).
These tests focus on the xyz (mod) directory to test as many things as possible. These tests mostly focus on code that does not require things that would only be found in the browser.
### Prerequisites

1. Setup
- Ensure that you have installed the node modules `npm install`
- Ensure that you have bun.sh installed in your enviroment
- To run the tests you can execute `npm run test`
The minimum requirements are:

2. Output
- You will find an output of a summary of all the past/failed tests.
* Node.js (version 18 and above)
* [Bun.sh](https://bun.sh) (version 1.1.0 and above for Codi v0.0.47)
* Node modules installed via `npm install`

## module (browser)
## Test Structure

The module tests are designed to make use of the browser environment by having full access to the DOM.
With these tests will have full access to the mapp library and a mapview for the loaded application. This allows us to not need to mock different module imports. Everything will be part of the build. All these tests also make use of the Codi test framework, and make use of the test runner, that is designed to run specifically in a view.
Tests are organized in the `/tests` directory with two main subdirectories:

To run these tests you will need to lauch the application with specific test settings. These will be provided only to GEOLYTIX developers.
* `/tests/mod`: CLI tests for the xyz (mod) directory
* `/tests/lib`: Module tests for browser environment

Launch the application and navigate to localhost:3000/test?template=test_view and open the console of the window.
You will see the output of the tests running in the console with any passes/failures.
Example structure:

## integrity
```

xyz/
├── mod/
│ ├── module1/
│ │ └── feature1.mjs
├── lib/
│ ├── module2/
│ │ └── feature2.mjs
├── tests/
│ ├── mod/
│ │ ├── module1/
│ │ │ ├── feature1.test.mjs
│ │ │ └── index.mjs
│ └── lib/
│ └── module2/
│ ├── feature2.test.mjs
│ └── index.mjs

```

Each test folder exports an object matching its corresponding directory structure:

```javascript
// tests/mod/module1/index.mjs
export default {
feature1: () => import('./feature1.test.mjs')
};
```

## Test Types

### 1. CLI (Console) Tests

CLI tests are vanilla JavaScript tests that execute in the Node.js runtime using the Codi Test framework. These tests focus on the xyz (mod) directory and code that doesn't require browser-specific features.

#### Running CLI Tests

```bash
npm run test
```

With quiet mode (v0.0.47+):

```bash
npm run test -- --quiet
```

### 2. Module (Browser) Tests

Module tests are designed for the browser environment with full access to:

The integrity tests are designed to check data integrity on deployed appications.
To run these test naviate to any deployed instance and ensure that you have `?template=test_view&integrity=true` is included in the params of the URL.
* DOM
* Mapp library
* Mapview for loaded application
* No mocking required for module imports

This will give you a similar output to the normal test_view.
#### Running Module Tests

## Writing tests
1. Launch the application with specific test settings (provided to GEOLYTIX developers)
2. Navigate to `localhost:3000/test?template=test_view`, just the base /test url if using settings
3. Open browser console to view test results, can also be viewed in vscode with the chrome debugger. (See developer notes)

When writing tests you will need to make use of different Codi elements to make up your test.
### 3. Integrity Tests

Include the describe and it functions. These are used as the building blocks to the tests. The describe block is used to describe the overall function and what the test suite is actually trying to achive. The It block is used to make more granular descriptions of different elements/functions of the code.
Integrity tests check data integrity on deployed applications.

In this example we describe what the main output should be. And then we assert the different elements that are part of the module.
Part of this code is the `assertTrue` function that is used to check if a value is true.
#### Running Integrity Tests

```js
1. Navigate to any deployed instance
2. Add `?template=test_view&integrity=true` to the URL parameters
3. View results in browser console

## Writing Tests

### Test Structure

Tests use the describe-it pattern for organization:

```javascript
import { describe, it, assertTrue } from 'codi';

describe('Feature Description', () => {
it('should behave in a specific way', () => {
// Test code
});
});
```

Example with multiple assertions:

```javascript
describe('All languages should have the same base language entries', () => {
...
Object.keys(mapp.dictionaries).forEach(language => {
it(`The ${language} dictionary should have all the base keys`, () => {
Object.keys(base_dictionary).forEach(key => {
assertTrue(!!mapp.dictionaries[language][key], `${language} should have ${key}`);
});
});
});
...
Object.keys(mapp.dictionaries).forEach(language => {
it(`The ${language} dictionary should have all the base keys`, () => {
Object.keys(base_dictionary).forEach(key => {
assertTrue(!!mapp.dictionaries[language][key],
`${language} should have ${key}`);
});
});
});
});
```

Assertion Functions 🧪
Codi provides several assertion functions to compare expected and actual values:
### Available Assertions

Codi provides several built-in assertions:

* `assertEqual(actual, expected, message)` ⚖️
* Asserts that the actual value equals the expected value
* `assertNotEqual(actual, expected, message)` 🙅‍♂️
* Asserts that the actual value does not equal the expected value
* `assertTrue(actual, message)` ✅
* Asserts that the actual value is true
* `assertFalse(actual, message)` ❌
* Asserts that the actual value is false
* `assertThrows(callback, errorMessage, message)` 💥
* Asserts that the callback throws an error with the specified message
* `assertNoDuplicates(callback, errorMessage, message)` 👬
* Asserts that there are no duplicates in a provided array.

### Test Output Control

Codi v0.0.47 features:

* `--quiet`: Shows only test failures (recommended for CI/CD pipelines)
* Default mode: Shows all test results with colorful output

## Best Practices

1. Maintain parallel structure between source and test directories
2. Use descriptive test names
3. One describe per test suite
4. Group related tests in the same describe block
5. Use test bundles for reusable configurations
6. Keep tests focused and isolated
7. Use `--quiet` flag in CI/CD pipelines. (can also be used on other test fuctions).

## Common Issues and Solutions

### Test Discovery

Codi automatically discovers tests in files with the pattern:

- `assertEqual(actual, expected, message)`: Asserts that the actual value is equal to the expected value. ⚖️
- `assertNotEqual(actual, expected, message)`: Asserts that the actual value is not equal to the expected value. 🙅‍♂️
- `assertTrue(actual, message)`: Asserts that the actual value is true. ✅
- `assertFalse(actual, message)`: Asserts that the actual value is false. ❌
- `assertThrows(callback, errorMessage, message)`: Asserts that the provided callback function throws an error with the specified error message. 💥
* `*.test.mjs`

### cli
### Error Handling

The directory that you need to add these cli tests are in the `tests/mod` directory. This directory needs to replicate the same kind of structure as the mod directory. Reason we do this is to ensure that test correlates to the position in the project that they are referencing.
If tests fail to run:

### module
1. Ensure Bun.sh version is compatible (v1.1.0+ for Codi v0.0.47)
2. Check file extensions are `.mjs`
3. Verify import/export syntax is ESM compatible
4. Confirm test directory structure matches source directories
5. Verify test settings in xyz_settings/tests/launch.json

The directory that you need to add these module tests are in the `tests/lib` directory.
For more information, please visit the [Codi GitHub repository](https://github.com/RobAndrewHurst/codi).
5 changes: 3 additions & 2 deletions esbuild.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ import * as esbuild from 'esbuild'
const isDev = process.env.NODE_ENV !== 'DEVELOPMENT';

const buildOptions = {
entryPoints: ['./lib/mapp.mjs', './lib/ui.mjs'],
entryPoints: isDev ? ['./lib/mapp.mjs', './lib/ui.mjs'] : ['./lib/mapp.mjs', './lib/ui.mjs', './tests/_mapp.test.mjs'],
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We bundle the test assets if we are in DEVELOPMENT mode. We can discuss if we want this all the time or in other certain situations.

bundle: true,
minify: isDev,
sourcemap: true,
sourceRoot: '/lib',
format: 'iife',
outdir: './public/js/lib',
outbase: '.',
outdir: 'public/js',
metafile: true,
logLevel: 'info'
};
Expand Down
2 changes: 1 addition & 1 deletion eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export default [
{
ignores: ['public/js/lib/*', 'docs/**/*'],
ignores: ['public/js/**/*', 'docs/**/*'],
},
{
files: ['**/*.js', '**/*.mjs'],
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"scripts": {
"test": "codi tests",
"_build": "node esbuild.config.mjs",
"browser-sync": "npx browser-sync start --proxy localhost:3000 --port 3001 --ui-port 3002 --files public/js/lib/**/* --no-open --no-notify",
"browser-sync": "npx browser-sync start --proxy localhost:3000 --port 3001 --ui-port 3002 --files public/js/**/* --no-open --no-notify",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Browser sync will now reload for either changes to lib or tests

"ui_css": "npx esbuild --bundle public/css/_ui.css --outfile=public/css/ui.css --loader:.svg=dataurl",
"mapp_css": "npx esbuild --bundle public/css/_mapp.css --outfile=public/css/mapp.css",
"generate-docs": "jsdoc --configure jsdoc_mapp.json --verbose && jsdoc --configure jsdoc_xyz.json --verbose && jsdoc --configure jsdoc_test.json --verbose",
Expand All @@ -33,7 +33,7 @@
"@eslint/js": "^9.13.0",
"browser-sync": "^3.0.3",
"clean-jsdoc-theme": "^4.3.0",
"codi-test-framework": "^0.0.33",
"codi-test-framework": "^0.0.47",
"concurrently": "^9.1.0",
"cookie-parser": "^1.4.5",
"dotenv": "^16.4.5",
Expand Down
31 changes: 14 additions & 17 deletions public/views/_test.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@
<script type="importmap">
{
"imports": {
"codi": "https://esm.sh/codi-test-framework@0.0.33"
"codi": "https://esm.sh/codi-test-framework@0.0.47"
}
}
</script>
} </script>

<script type="module" src="{{dir}}/public/js/lib/mapp.js" defer></script>
<script type="module" src="{{dir}}/public/js/lib/ui.js" defer></script>
<script type="module" src="{{dir}}/public/js/tests/_mapp.test.js" defer></script>

<style>
html {
Expand Down Expand Up @@ -309,23 +309,14 @@
</div>

<script type="module">
import { runWebTests, it, describe, assertEqual, assertNotEqual, assertTrue, assertFalse, assertNoDuplicates } from "codi";

const _codi = {
runWebTests,
describe,
it,
assertEqual,
assertNotEqual,
assertTrue,
assertFalse,
assertNoDuplicates
};

import * as _codi from "codi";

self.codi = _codi;

const urlParams = new URLSearchParams(window.location.search);
const testFiles = urlParams.get('integrity') ? [`${window.location.origin}{{dir}}/public/tests/integrity.test.mjs`] : [`${window.location.origin}{{dir}}/tests/browser/local.test.mjs`];
const integrity = urlParams.get('integrity');
const testFiles = integrity ? [`${window.location.origin}{{dir}}/public/tests/integrity.test.mjs`] : [`${window.location.origin}{{dir}}/tests/browser/local.test.mjs`];

try {

Expand Down Expand Up @@ -353,7 +344,13 @@
}
}

let results = await runWebTests(testFiles);
let results = {};

if (integrity) {
results = await codi.runWebTests(testFiles);
} else {
results = await codi.runWebTestFunction(_mappTest.coreTest);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This webTestFunction might be better used on each test function in the test bundle to try aid parallel testing in the future.

}

const shouldDownload = urlParams.get('download') === 'true';

Expand Down
Loading
Loading