1 | // Copyright IBM Corp. 2018. All Rights Reserved.
|
2 | // Node module: @loopback/testlab
|
3 | // This file is licensed under the MIT License.
|
4 | // License text available at https://opensource.org/licenses/MIT
|
5 |
|
6 | import {
|
7 | appendFile,
|
8 | copy,
|
9 | emptyDir,
|
10 | ensureDir,
|
11 | ensureDirSync,
|
12 | pathExists,
|
13 | remove,
|
14 | writeJson,
|
15 | } from 'fs-extra';
|
16 | import {parse, resolve} from 'path';
|
17 |
|
18 | /**
|
19 | * TestSandbox class provides a convenient way to get a reference to a
|
20 | * sandbox folder in which you can perform operations for testing purposes.
|
21 | */
|
22 | export class TestSandbox {
|
23 | // Path of the TestSandbox
|
24 | private _path?: string;
|
25 |
|
26 | public get path(): string {
|
27 | if (!this._path) {
|
28 | throw new Error(
|
29 | `TestSandbox instance was deleted. Create a new instance.`,
|
30 | );
|
31 | }
|
32 | return this._path;
|
33 | }
|
34 |
|
35 | /**
|
36 | * Will create a directory if it doesn't already exist. If it exists, you
|
37 | * still get an instance of the TestSandbox.
|
38 | *
|
39 | * @param path - Path of the TestSandbox. If relative (it will be resolved relative to cwd()).
|
40 | */
|
41 | constructor(path: string) {
|
42 | // resolve ensures path is absolute / makes it absolute (relative to cwd())
|
43 | this._path = resolve(path);
|
44 | ensureDirSync(this.path);
|
45 | }
|
46 |
|
47 | /**
|
48 | * Returns the path of the TestSandbox
|
49 | */
|
50 | getPath(): string {
|
51 | return this.path;
|
52 | }
|
53 |
|
54 | /**
|
55 | * Resets the TestSandbox. (Remove all files in it).
|
56 | */
|
57 | async reset(): Promise<void> {
|
58 | // Decache files from require's cache so future tests aren't affected incase
|
59 | // a file is recreated in sandbox with the same file name but different
|
60 | // contents after resetting the sandbox.
|
61 | for (const key in require.cache) {
|
62 | if (key.startsWith(this.path)) {
|
63 | delete require.cache[key];
|
64 | }
|
65 | }
|
66 |
|
67 | await emptyDir(this.path);
|
68 | }
|
69 |
|
70 | /**
|
71 | * Deletes the TestSandbox.
|
72 | */
|
73 | async delete(): Promise<void> {
|
74 | await remove(this.path);
|
75 | delete this._path;
|
76 | }
|
77 |
|
78 | /**
|
79 | * Makes a directory in the TestSandbox
|
80 | *
|
81 | * @param dir - Name of directory to create (relative to TestSandbox path)
|
82 | */
|
83 | async mkdir(dir: string): Promise<void> {
|
84 | await ensureDir(resolve(this.path, dir));
|
85 | }
|
86 |
|
87 | /**
|
88 | * Copies a file from src to the TestSandbox. If copying a `.js` file which
|
89 | * has an accompanying `.js.map` file in the src file location, the dest file
|
90 | * will have its sourceMappingURL updated to point to the original file as
|
91 | * an absolute path so you don't need to copy the map file.
|
92 | *
|
93 | * @param src - Absolute path of file to be copied to the TestSandbox
|
94 | * @param dest - Optional. Destination filename of the copy operation
|
95 | * (relative to TestSandbox). Original filename used if not specified.
|
96 | */
|
97 | async copyFile(src: string, dest?: string): Promise<void> {
|
98 | dest = dest
|
99 | ? resolve(this.path, dest)
|
100 | : resolve(this.path, parse(src).base);
|
101 |
|
102 | await copy(src, dest);
|
103 |
|
104 | if (parse(src).ext === '.js' && pathExists(src + '.map')) {
|
105 | const srcMap = src + '.map';
|
106 | await appendFile(dest, `\n//# sourceMappingURL=${srcMap}`);
|
107 | }
|
108 | }
|
109 |
|
110 | /**
|
111 | * Creates a new file and writes the given data serialized as JSON.
|
112 | *
|
113 | * @param dest - Destination filename, optionally including a relative path.
|
114 | * @param data - The data to write.
|
115 | */
|
116 | async writeJsonFile(dest: string, data: unknown): Promise<void> {
|
117 | dest = resolve(this.path, dest);
|
118 | const destDir = parse(dest).dir;
|
119 | await ensureDir(destDir);
|
120 | return writeJson(dest, data, {spaces: 2});
|
121 | }
|
122 | }
|