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