@gmjs/test-util v0.0.14
Test Utilities
This project contains utilities for writing file comparison tests.
Installation
npm install --save-dev @gmjs/test-util
API
File Test Functions
High-level functions to do the heavy lifting in file comparison tests.
createFsTestCases
Creates test cases for file system tests.
These are essentially tests for code that generates a file hierarchy. We can use it to specify expected file hierarchy that should result from the code under test, and compare it to the actual file hierarchy generated by the code under test.
So how do we do that?
First of all, this function expects to find a directory with test cases as described in Test Case File Structure Setup.
Parameters
The first parameter to the function, testCasesRoot
specifies the path to the above mentioned directory (<test-cases-root>
).
The second parameter, actualFunction
is the actual code under test. You define this function yourself, and pass it to createFsTestCases()
. Actual function will receive the full path to the test case directory, and it should return a promise that resolves to a FilesContainer object.
Return Value
The result of createFsTestCases()
is an array of TestCaseRun
objects:
interface TestCaseRun {
readonly name: string;
readonly run: () => Promise<TestComparisonStrings>;
}
interface TestComparisonStrings {
readonly expected: string;
readonly actual: string;
}
name
is simply the name of the test case directory. For example, if the test case directory is <test-cases-root>/cases/test-case-01
, then name
will be test-case-01
.
Running Tests
The actual test is executed by calling the run()
function returned from createFsTestCases()
. This function returns a promise, which resolves to a TestComparisonStrings
object, which can be used for a simple string comparison test.
expected
and actual
strings are generated internally from the expected
and actual
FilesContainer objects. The string will contain a list of files, each with its path and content. The list of files is sorted by path, so that the order of files is deterministic. For binary files, only the hash is printed, not the actual content.
Example
describe('test', () => {
const testCaseRuns = createFsTestCases('path/to/test-cases-root', getActualFiles);
for (const testCaseRun of testCaseRuns) {
it(testCaseRun.name, async () => {
const { expected, actual } = await testCaseRun.run();
expect(actual).toBe(expected);
});
}
});
async function getActualFiles(testCaseDirectory: string): Promise<FilesContainer> {
// code under test, returns a FilesContainer object
}
Test Case File Structure Setup
This is the test case file structure used by createFsTestCases function.
This function expects to find a directory with test case files which will produce actual
and expected
results for a final test comparison.
Below is an example with a single test case, and a single shared directory.
<test-cases-root>
cases
test-case-01
expected
files
subdir
expected-11.png.bin
expected-1.js.txt
expected-2.json.txt
path-mapping.json
input
input.json
shared
shared-dir
shared-1.js.txt
shared-2.ts.txt
Test Case
Test cases can have arbitrary names, not just test-case-*
.
There can be any number of test cases in <test-cases-root>/cases
directory.
<test-case>/expected/files
directory contains files that will be used to create the expected
result, this is the input for the mapping defined in path-mapping.json
.
Each file under <test-case>/expected/files
must have a .txt
or .bin
extension, which determines whether the file is read as text or binary.
<test-case>/expected/path-mapping.json
file contains a list of fr
-to
entries, which are used to map files from <test-case>/expected/files
to the final expected
file system structure.
Besides <test-case>/expected
directory, you can put any other directories and files inside <test-case>
directory. You can for example use these as inputs for creating the actual
result in case you need any inputs. Then you can read those inputs inside actualFunction
. However, since you provide actualFunction
yourself, you have full control of this process and you can generate the actual
result in any way you want, using any inputs you want.
Shared Directories
<test-cases-root>/shared
directory contains a list of shared directories, each with shared files that can be referenced from any test case. These are essentially shared expected
files, ones which would otherwise be inside <test-case>/expected/files
, but are moved to a shared directory to avoid duplication. Like files under <test-case>/expected/files
, files under <test-cases-root>/shared
must also have a .txt
or .bin
extension.
Directories under <test-cases-root>/shared
can have arbitrary names. Let's say we have the following directory structure:
<test-cases-root>
cases
...
shared
a-shared-dir
shared-1.js.txt
shared-2.ts.txt
another-shared-dir
shared-3.json.txt
shared-4.svg.txt
Then, in path-mapping.json
files, you can reference files under <test-cases-root>/shared
like this (<a-shared-dir>
etc. are literal, verbatim values in the mapping json file, not documentation placeholders like <test-cases-root>
):
[
{
"group": "some-group",
"files": [
{
"fr": "<a-shared-dir>/shared-1.js.txt",
"to": "target-dir/some-file.js"
},
{
"fr": "<another-shared-dir>/shared-3.json.txt",
"to": "target-dir/some-other-file.json"
}
]
}
]
Note that for example <a-shared-dir>
refers to <test-cases-root>/shared/a-shared-dir
.
<test-cases-root>/shared
is optional, and you don't have to create it if you don't use it in path-mapping.json
files.
Test Case Path Mapping
<test-case>/expected/path-mapping.json
file contains a list of fr
-to
entries, which are used to map files from <test-case>/expected/files
to the final expected
file system structure.
More precisely, path-mapping.json
will contain groups of fr
-to
entries, but these groups are arbitrary, used just for visual organization of the file, and the lists of fr
-to
entries are simply flattened into a single list.
Using the first directory structure example, we can have the following file mapping:
[
{
"group": "assets",
"files": [
{
"fr": "subdir/expected-11.png.bin",
"to": "target-dir/assets/asset-11.png"
}
]
},
{
"group": "src",
"files": [
{
"fr": "expected-1.js.txt",
"to": "target-dir/src/file-1.js"
},
{
"fr": "expected-2.json.txt",
"to": "target-dir/src/file-2.json"
},
{
"fr": "<shared-dir>/shared-1.js.txt",
"to": "target-dir/src/shared-file-1.js"
},
{
"fr": "<shared-dir>/shared-2.ts.txt",
"to": "target-dir/src/shared-file-2.ts"
}
]
}
]
You can think of the above as a flat list of 5 file mappings, which will produce the following file structure in the expected
result:
target-dir
assets
asset-11.png
src
file-1.js
file-2.json
shared-file-1.js
shared-file-2.ts
Types
FilesContainer
Container with file path-content pairs.
Has the following structure:
interface FilesContainer {
readonly textFiles: readonly FilePathTextContent[];
readonly binaryFiles: readonly FilePathBinaryContent[];
}
Items in the above array have essentially the following structure:
interface FilePathTextContent {
readonly path: string;
readonly content: string;
}
interface FilePathBinaryContent {
readonly path: string;
readonly content: Buffer;
}
9 months ago
9 months ago
8 months ago
8 months ago
8 months ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago