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

Fix compatibility with Node 20 #384

Open
ademdinarevic opened this issue Dec 21, 2023 · 10 comments
Open

Fix compatibility with Node 20 #384

ademdinarevic opened this issue Dec 21, 2023 · 10 comments

Comments

@ademdinarevic
Copy link

Since we upgraded to node 20.10.0 the method for mocking file creation doesn't work properly

const mockFs = require("mock-fs");

mockFs({
  "/file": {
     VAR: "var_value",
  },
});
@zbigg
Copy link

zbigg commented Dec 22, 2023

For anyone trying to fix it, it's caused by node20 to use new binding method: readFileUtf8 which isn't "mocked" by this module.

It looks like we should just implement open+read+toUtf+close as one function here.

The usage:

https://github.com/nodejs/node/blob/a81788cb27b96579343765eeba07fd2b772829d0/lib/fs.js#L456

Implementation

https://github.com/nodejs/node/blob/a81788cb27b96579343765eeba07fd2b772829d0/src/node_file.cc#L2375

This is very naive and PoC binding that fixes readFileSync(..., 'utf8') for node 20

Binding.prototype.readFileUtf8 = function (
  name,
  flags
) {
    const fd = this.open(name, flags);
    const descriptor = this.getDescriptorById(fd);

    if (!descriptor.isRead()) {
      throw new FSError('EBADF');
    }
    const file = descriptor.getItem();
    if (file instanceof Directory) {
      throw new FSError('EISDIR');
    }
    if (!(file instanceof File)) {
      // deleted or not a regular file
      throw new FSError('EBADF');
    }
    const content = file.getContent();
    return content.toString('utf8');
};

@canhassancode
Copy link

canhassancode commented Dec 22, 2023

Facing the exact same issue, it was working completely fine then randomly broke. Below is an example of the setup:

beforeAll(() => {
  mockFs.restore();
});

afterEach(() => {
  mockFs.restore();
});

it('mock test', () => {
  mockFs({
    './profiles': {
      test: {
        'TEST_PROFILE_1.json': JSON.stringify(testProfile1),
        'TEST_PROFILE_2.json': JSON.stringify(testProfile2),
      },
...
    },
  });
});

Getting the error: ENOENT: no such file or directory, open 'profiles/test/TEST_PROFILE_2.json'

@crfrolik
Copy link

For those like me who came here in a panic because their tests started failing, I had success (and minimal effort) replacing mock-fs with memfs.

@PedroS11
Copy link

PedroS11 commented Feb 6, 2024

For those like me who came here in a panic because their tests started failing, I had success (and minimal effort) replacing mock-fs with memfs.

Do you mind post some examples please? I'm trying to use memfs but I'm struggling with it. I found an basic example with jest however, i'm using chai so nothing is working

For reference, this is the basic example of memfs using jest: https://medium.com/nerd-for-tech/testing-in-node-js-easy-way-to-mock-filesystem-883b9f822ea4

or https://dev.to/julienp/node-js-testing-using-a-virtual-filesystem-as-a-mock-2jln

@JBloss1517
Copy link

@PedroS11 I also decided to switch using memfs. For me, it was mostly a matter of switching mockFS({. . .}) with vol.fromNestedJSON({. . .}) and changing mockFS.restore() to vol.reset()

So for example (I am using vitest in this but jest should be pretty close as well)

Using mock-fs

const mockFS = require("mock-fs");
import { afterEach, beforeEach, describe, expect, test } from "vitest";

describe("some test", () => {
  beforeEach(() => {
    mockFS({
      Projects: {
        "Projects - 23": {
          "Test Client": {
            "Projects 1": {
              "Sub folder": {},
            },
          },
        },
        "Projects - 24": {},
      },
    });
  });

  afterEach(() => {
    mockFS.restore();
  });

  test("project folder creation", () => {
    const projectFolderPath = path.join(
      "Projects",
      "Projects - 23",
      "Test Client",
      "Projects 2",
    );
    const result = _createProjectFolder(projectFolderPath, projectTemplatePath);
    expect(result).toEqual({ path: projectFolderPath, error: null });
  });
});

Using memfs

import { fs, vol } from "memfs";
import { afterEach, beforeEach, describe, expect, test } from "vitest";

describe("some test", () => {
  beforeEach(() => {
    vol.fromNestedJSON({   // <-- Changed this from mockFS
      Projects: {
        "Projects - 23": {
          "Test Client": {
            "Projects 1": {
              "Sub folder": {},
            },
          },
        },
        "Projects - 24": {},
      },
    });
  });

  afterEach(() => {
    vol.reset(); // <-- changed this from mockFS.restore();
  });

  test("project folder creation", () => {
    const projectFolderPath = path.join(
      "Projects",
      "Projects - 23",
      "Test Client",
      "Projects 2",
    );
    const result = _createProjectFolder(projectFolderPath, projectTemplatePath);
    expect(result).toEqual({ path: projectFolderPath, error: null });
  });
});

I hope that helps!

@PedroS11
Copy link

PedroS11 commented Feb 24, 2024

@PedroS11 I also decided to switch using memfs. For me, it was mostly a matter of switching mockFS({. . .}) with vol.fromNestedJSON({. . .}) and changing mockFS.restore() to vol.reset()

So for example (I am using vitest in this but jest should be pretty close as well)

Using mock-fs

const mockFS = require("mock-fs");
import { afterEach, beforeEach, describe, expect, test } from "vitest";

describe("some test", () => {
  beforeEach(() => {
    mockFS({
      Projects: {
        "Projects - 23": {
          "Test Client": {
            "Projects 1": {
              "Sub folder": {},
            },
          },
        },
        "Projects - 24": {},
      },
    });
  });

  afterEach(() => {
    mockFS.restore();
  });

  test("project folder creation", () => {
    const projectFolderPath = path.join(
      "Projects",
      "Projects - 23",
      "Test Client",
      "Projects 2",
    );
    const result = _createProjectFolder(projectFolderPath, projectTemplatePath);
    expect(result).toEqual({ path: projectFolderPath, error: null });
  });
});

Using memfs

import { fs, vol } from "memfs";
import { afterEach, beforeEach, describe, expect, test } from "vitest";

describe("some test", () => {
  beforeEach(() => {
    vol.fromNestedJSON({   // <-- Changed this from mockFS
      Projects: {
        "Projects - 23": {
          "Test Client": {
            "Projects 1": {
              "Sub folder": {},
            },
          },
        },
        "Projects - 24": {},
      },
    });
  });

  afterEach(() => {
    vol.reset(); // <-- changed this from mockFS.restore();
  });

  test("project folder creation", () => {
    const projectFolderPath = path.join(
      "Projects",
      "Projects - 23",
      "Test Client",
      "Projects 2",
    );
    const result = _createProjectFolder(projectFolderPath, projectTemplatePath);
    expect(result).toEqual({ path: projectFolderPath, error: null });
  });
});

I hope that helps!

Thank you for this snippet @JBloss1517 !! in the following weeks, I'll try to migrate too and I'll see how it works. I'll leave my progress here when I start so it helps everyone with this struggle

polomsky added a commit to SuitestAutomation/suitest-js-api that referenced this issue Mar 6, 2024
Currently, can not use Node.js >= 20.10 because mock-fs used in the unit
tests does not support it. tschaub/mock-fs#384
polomsky added a commit to SuitestAutomation/suitest-js-api that referenced this issue Mar 6, 2024
Currently, can not use Node.js >= 20.9 because mock-fs used in the unit
tests does not support it. tschaub/mock-fs#384
polomsky added a commit to SuitestAutomation/suitest-js-api that referenced this issue Mar 6, 2024
Currently, can not use Node.js >= 20.8 because mock-fs used in the unit
tests does not support it. tschaub/mock-fs#384
polomsky added a commit to SuitestAutomation/suitest-js-api that referenced this issue Mar 6, 2024
Currently, can not use Node.js >= 20.8 because mock-fs used in the unit
tests does not support it. tschaub/mock-fs#384
polomsky added a commit to SuitestAutomation/suitest-js-api that referenced this issue Mar 6, 2024
Currently, can not use Node.js >= 20.8 because mock-fs used in the unit
tests does not support it. tschaub/mock-fs#384
@dennismeister93
Copy link

dennismeister93 commented Mar 10, 2024

hi @tschaub, are there any plans to work on this?

@tschaub
Copy link
Owner

tschaub commented Mar 18, 2024

@dennismeister93 - I would be happy to review a pull request, but don't have plans to spend time on a fix myself.

@dicolasi
Copy link

struggling with the same problem here.

@MichaelSitter
Copy link

MichaelSitter commented May 7, 2024

I was having issues with my Jest tests still using the built-in fs instead of the memfs implementation. This worked to mock out fs & fs/promises:

import { fs, vol } from 'memfs'
// import needs to match what you used in your code
jest.mock('fs', () => fs)
jest.mock('fs/promises', () => fs.promises)

// test code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

10 participants