aboutsummaryrefslogtreecommitdiffhomepage
path: root/test/fixtures.ts
blob: 2c498ac07d8ddee6e509f57df3b71c8326640194 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
import fs from 'node:fs';
import type { PathLike, Stats } from 'node:fs';
import callsite from 'callsite';
import type { DirectoryJSON } from 'memfs';
import { fs as memfs, vol } from 'memfs';
import type { TDataOut } from 'memfs/lib/encoding';
import upath from 'upath';

const realFs = fs; //jest.requireActual<typeof fs>('fs');

/**
 * Class to work with in-memory file-system
 */
export class Fixtures {
  /**
   * Returns content from fixture file from __fixtures__ folder
   * @param name name of the fixture file
   * @param [fixturesRoot] - Where to find the fixtures, uses the current test folder by default
   * @returns
   */
  static get(name: string, fixturesRoot = '.'): string {
    return realFs.readFileSync(
      upath.resolve(Fixtures.getPathToFixtures(fixturesRoot), name),
      {
        encoding: 'utf-8',
      },
    );
  }

  /**
   *  Returns path to fixture file in __fixtures__ folder
   * @param name name of the fixture file
   * @param [fixturesRoot] - Where to find the fixtures, uses the current test folder by default
   * @return path to the fixture
   */
  static getPath(name: string, fixturesRoot = '.'): string {
    return upath.resolve(Fixtures.getPathToFixtures(fixturesRoot), name);
  }

  /**
   * Returns content from fixture file from __fixtures__ folder as `Buffer`
   * @param name name of the fixture file
   * @param [fixturesRoot] - Where to find the fixtures, uses the current test folder by default
   * @returns
   */
  static getBinary(name: string, fixturesRoot = '.'): Buffer {
    return realFs.readFileSync(
      upath.resolve(Fixtures.getPathToFixtures(fixturesRoot), name),
    );
  }

  /**
   * Returns content from fixture file from __fixtures__ folder and parses as JSON
   * @param name name of the fixture file
   * @param [fixturesRoot] - Where to find the fixtures, uses the current test folder by default
   * @returns
   */
  static getJson<T = any>(name: string, fixturesRoot = '.'): T {
    return JSON.parse(
      realFs.readFileSync(
        upath.resolve(Fixtures.getPathToFixtures(fixturesRoot), name),
        {
          encoding: 'utf-8',
        },
      ),
    ) as T;
  }

  /**
   * Adds files from a flat json object to the file-system
   * @param json flat object
   * @param cwd is an optional string used to compute absolute file paths, if a file path is given in a relative form
   */
  static mock(json: DirectoryJSON, cwd?: string): void {
    vol.fromJSON(json, cwd);
  }

  /**
   * Exports the whole contents of the volume recursively to a flat JSON object
   * @param paths is an optional argument that specifies one or more paths to be exported. If this argument is omitted, the whole volume is exported. paths can be an array of paths. A path can be a string, Buffer or an URL object.
   * @param json is an optional object parameter which will be populated with the exported files
   * @param isRelative is boolean that specifies if returned paths should be relative
   * @returns
   */
  static toJSON(
    paths?: PathLike | PathLike[],
    json?: Record<string, unknown>,
    isRelative?: boolean,
  ): DirectoryJSON {
    return vol.toJSON(paths, json, isRelative);
  }

  /**
   * Removes all files from the volume.
   */
  static reset(): void {
    vol.reset();
    fsExtraMock.pathExists.mockImplementation(pathExists);
    fsExtraMock.remove.mockImplementation(memfs.promises.rm);
    fsExtraMock.removeSync.mockImplementation(memfs.rmSync);
    fsExtraMock.readFile.mockImplementation(readFile);
    fsExtraMock.writeFile.mockImplementation(memfs.promises.writeFile);
    fsExtraMock.outputFile.mockImplementation(outputFile);
    fsExtraMock.stat.mockImplementation(stat);
    fsExtraMock.chmod.mockImplementation(memfs.promises.chmod);
  }

  private static getPathToFixtures(fixturesRoot = '.'): string {
    const stack = callsite();
    const callerDir = upath.dirname(stack[2].getFileName());
    return upath.resolve(callerDir, fixturesRoot, '__fixtures__');
  }
}

const fsExtraMock = {
  pathExists: jest.fn(),
  remove: jest.fn(),
  removeSync: jest.fn(),
  readFile: jest.fn(),
  writeFile: jest.fn(),
  outputFile: jest.fn(),
  stat: jest.fn(),
  chmod: jest.fn(),
};

// Temporary solution, when all tests will be rewritten to Fixtures mocks can be moved into __mocks__ folder
export function fsExtra(): any {
  return {
    ...memfs,
    ...fsExtraMock,
  };
}

export function readFile(fileName: string, options: any): Promise<TDataOut> {
  if (fileName.endsWith('.wasm') || fileName.endsWith('.wasm.gz')) {
    return fs.promises.readFile(fileName, options);
  }

  return memfs.promises.readFile(fileName, options);
}

export async function outputFile(
  file: string,
  data: string | Buffer | Uint8Array,
): Promise<void> {
  const dir = upath.dirname(file);

  if (await pathExists(dir)) {
    await memfs.promises.writeFile(file, data);
  } else {
    await memfs.promises.mkdir(dir, {
      recursive: true,
    });
    await memfs.promises.writeFile(file, data);
  }
}

async function pathExists(path: string): Promise<boolean> {
  try {
    await memfs.promises.access(path);
    return true;
  } catch {
    return false;
  }
}

async function stat(path: string): Promise<Stats> {
  // memfs type mismatch
  return (await memfs.promises.stat(path)) as Stats;
}