From b63cce88ad5ba361b6b94382f41166609a2e152f Mon Sep 17 00:00:00 2001
From: Paul Slaughter <pslaughter@gitlab.com>
Date: Fri, 20 Nov 2020 16:20:45 -0600
Subject: [PATCH] Disallow fs in FE integration specs

- This is because we'd like to run these in the browser
- Also removes a potential circular dependency
when loading specs in the browser
---
 jest.config.base.js                           |  6 ++-
 spec/frontend_integration/.eslintrc.yml       |  4 ++
 .../ide/helpers/ide_helper.js                 | 17 ------
 .../frontend_integration/ide/helpers/start.js | 17 ++++++
 .../ide/ide_integration_spec.js               |  5 +-
 .../ide/user_opens_ide_spec.js                | 17 +++---
 .../test_helpers/fixtures.js                  | 52 ++++++++++++-------
 7 files changed, 71 insertions(+), 47 deletions(-)
 create mode 100644 spec/frontend_integration/ide/helpers/start.js

diff --git a/jest.config.base.js b/jest.config.base.js
index 9f6117757766e..40f16c6d12449 100644
--- a/jest.config.base.js
+++ b/jest.config.base.js
@@ -30,6 +30,8 @@ module.exports = path => {
     testMatch = testMatch.map(path => path.replace('_spec.js', ''));
   }
 
+  const TEST_FIXTURES_PATTERN = 'test_fixtures(/.*)$';
+
   const moduleNameMapper = {
     '^~(/.*)$': '<rootDir>/app/assets/javascripts$1',
     '^ee_component(/.*)$':
@@ -38,12 +40,12 @@ module.exports = path => {
     '^ee_else_ce(/.*)$': '<rootDir>/app/assets/javascripts$1',
     '^helpers(/.*)$': '<rootDir>/spec/frontend/helpers$1',
     '^vendor(/.*)$': '<rootDir>/vendor/assets/javascripts$1',
+    [TEST_FIXTURES_PATTERN]: '<rootDir>/tmp/tests/frontend/fixtures$1',
     '\\.(jpg|jpeg|png|svg|css)$': '<rootDir>/spec/frontend/__mocks__/file_mock.js',
     'emojis(/.*).json': '<rootDir>/fixtures/emojis$1.json',
     '^spec/test_constants$': '<rootDir>/spec/frontend/helpers/test_constants',
     '^jest/(.*)$': '<rootDir>/spec/frontend/$1',
     'test_helpers(/.*)$': '<rootDir>/spec/frontend_integration/test_helpers$1',
-    'test_fixtures(/.*)$': '<rootDir>/tmp/tests/frontend/fixtures$1',
   };
 
   const collectCoverageFrom = ['<rootDir>/app/assets/javascripts/**/*.{js,vue}'];
@@ -55,7 +57,7 @@ module.exports = path => {
       '^ee_component(/.*)$': rootDirEE,
       '^ee_else_ce(/.*)$': rootDirEE,
       '^ee_jest/(.*)$': '<rootDir>/ee/spec/frontend/$1',
-      'test_fixtures(/.*)$': '<rootDir>/tmp/tests/frontend/fixtures-ee$1',
+      [TEST_FIXTURES_PATTERN]: '<rootDir>/tmp/tests/frontend/fixtures-ee$1',
     });
 
     collectCoverageFrom.push(rootDirEE.replace('$1', '/**/*.{js,vue}'));
diff --git a/spec/frontend_integration/.eslintrc.yml b/spec/frontend_integration/.eslintrc.yml
index 2460e218f5968..8fff491bdcf85 100644
--- a/spec/frontend_integration/.eslintrc.yml
+++ b/spec/frontend_integration/.eslintrc.yml
@@ -4,5 +4,9 @@ settings:
   import/resolver:
     jest:
       jestConfigFile: 'jest.config.integration.js'
+rules:
+  no-restricted-imports:
+    - error
+    - fs
 globals:
   mockServer: false
diff --git a/spec/frontend_integration/ide/helpers/ide_helper.js b/spec/frontend_integration/ide/helpers/ide_helper.js
index 7ace586fe08d2..67355025727c5 100644
--- a/spec/frontend_integration/ide/helpers/ide_helper.js
+++ b/spec/frontend_integration/ide/helpers/ide_helper.js
@@ -1,8 +1,4 @@
-import { TEST_HOST } from 'helpers/test_constants';
 import { findAllByText, fireEvent, getByLabelText, screen } from '@testing-library/dom';
-import { initIde } from '~/ide';
-import extendStore from '~/ide/stores/extend';
-import { IDE_DATASET } from './mock_data';
 
 const isFolderRowOpen = row => row.matches('.folder.is-open');
 
@@ -135,16 +131,3 @@ export const commit = async () => {
 
   screen.getByText('Commit').click();
 };
-
-export const createIdeComponent = (container, { isRepoEmpty = false, path = '' } = {}) => {
-  global.jsdom.reconfigure({
-    url: `${TEST_HOST}/-/ide/project/gitlab-test/lorem-ipsum${
-      isRepoEmpty ? '-empty' : ''
-    }/tree/master/-/${path}`,
-  });
-
-  const el = document.createElement('div');
-  Object.assign(el.dataset, IDE_DATASET);
-  container.appendChild(el);
-  return initIde(el, { extendStore });
-};
diff --git a/spec/frontend_integration/ide/helpers/start.js b/spec/frontend_integration/ide/helpers/start.js
new file mode 100644
index 0000000000000..9dc9649e1bfc9
--- /dev/null
+++ b/spec/frontend_integration/ide/helpers/start.js
@@ -0,0 +1,17 @@
+import { TEST_HOST } from 'helpers/test_constants';
+import extendStore from '~/ide/stores/extend';
+import { IDE_DATASET } from './mock_data';
+import { initIde } from '~/ide';
+
+export default (container, { isRepoEmpty = false, path = '' } = {}) => {
+  global.jsdom.reconfigure({
+    url: `${TEST_HOST}/-/ide/project/gitlab-test/lorem-ipsum${
+      isRepoEmpty ? '-empty' : ''
+    }/tree/master/-/${path}`,
+  });
+
+  const el = document.createElement('div');
+  Object.assign(el.dataset, IDE_DATASET);
+  container.appendChild(el);
+  return initIde(el, { extendStore });
+};
diff --git a/spec/frontend_integration/ide/ide_integration_spec.js b/spec/frontend_integration/ide/ide_integration_spec.js
index 9d515d207493c..a17f57e221612 100644
--- a/spec/frontend_integration/ide/ide_integration_spec.js
+++ b/spec/frontend_integration/ide/ide_integration_spec.js
@@ -3,6 +3,7 @@ import waitForPromises from 'helpers/wait_for_promises';
 import { useOverclockTimers } from 'test_helpers/utils/overclock_timers';
 import { createCommitId } from 'test_helpers/factories/commit_id';
 import * as ideHelper from './helpers/ide_helper';
+import startWebIDE from './helpers/start';
 
 describe('WebIDE', () => {
   useOverclockTimers();
@@ -21,7 +22,7 @@ describe('WebIDE', () => {
   });
 
   it('user commits changes', async () => {
-    vm = ideHelper.createIdeComponent(container);
+    vm = startWebIDE(container);
 
     await ideHelper.createFile('foo/bar/test.txt', 'Lorem ipsum dolar sit');
     await ideHelper.deleteFile('foo/bar/.gitkeep');
@@ -55,7 +56,7 @@ describe('WebIDE', () => {
   });
 
   it('user adds file that starts with +', async () => {
-    vm = ideHelper.createIdeComponent(container);
+    vm = startWebIDE(container);
 
     await ideHelper.createFile('+test', 'Hello world!');
     await ideHelper.openFile('+test');
diff --git a/spec/frontend_integration/ide/user_opens_ide_spec.js b/spec/frontend_integration/ide/user_opens_ide_spec.js
index 958cab483eb28..645d2661b1803 100644
--- a/spec/frontend_integration/ide/user_opens_ide_spec.js
+++ b/spec/frontend_integration/ide/user_opens_ide_spec.js
@@ -1,6 +1,7 @@
 import { useOverclockTimers } from 'test_helpers/utils/overclock_timers';
 import { findByText, screen } from '@testing-library/dom';
 import * as ideHelper from './helpers/ide_helper';
+import startWebIDE from './helpers/start';
 
 describe('IDE: User opens IDE', () => {
   useOverclockTimers();
@@ -19,14 +20,14 @@ describe('IDE: User opens IDE', () => {
   });
 
   it('shows loading indicator while the IDE is loading', async () => {
-    vm = ideHelper.createIdeComponent(container);
+    vm = startWebIDE(container);
 
     expect(container.querySelectorAll('.multi-file-loading-container')).toHaveLength(3);
   });
 
   describe('when the project is empty', () => {
     beforeEach(() => {
-      vm = ideHelper.createIdeComponent(container, { isRepoEmpty: true });
+      vm = startWebIDE(container, { isRepoEmpty: true });
     });
 
     it('shows "No files" in the left sidebar', async () => {
@@ -42,7 +43,7 @@ describe('IDE: User opens IDE', () => {
 
   describe('when the file tree is loaded', () => {
     beforeEach(async () => {
-      vm = ideHelper.createIdeComponent(container);
+      vm = startWebIDE(container);
 
       await screen.findByText('README'); // wait for file tree to load
     });
@@ -76,7 +77,7 @@ describe('IDE: User opens IDE', () => {
 
   describe('a path to a text file is present in the URL', () => {
     beforeEach(async () => {
-      vm = ideHelper.createIdeComponent(container, { path: 'README.md' });
+      vm = startWebIDE(container, { path: 'README.md' });
 
       // a new tab is open for README.md
       await findByText(document.querySelector('.multi-file-edit-pane'), 'README.md');
@@ -89,7 +90,7 @@ describe('IDE: User opens IDE', () => {
 
   describe('a path to a binary file is present in the URL', () => {
     beforeEach(async () => {
-      vm = ideHelper.createIdeComponent(container, { path: 'Gemfile.zip' });
+      vm = startWebIDE(container, { path: 'Gemfile.zip' });
 
       // a new tab is open for Gemfile.zip
       await findByText(document.querySelector('.multi-file-edit-pane'), 'Gemfile.zip');
@@ -105,7 +106,7 @@ describe('IDE: User opens IDE', () => {
 
   describe('a path to an image is present in the URL', () => {
     beforeEach(async () => {
-      vm = ideHelper.createIdeComponent(container, { path: 'files/images/logo-white.png' });
+      vm = startWebIDE(container, { path: 'files/images/logo-white.png' });
 
       // a new tab is open for logo-white.png
       await findByText(document.querySelector('.multi-file-edit-pane'), 'logo-white.png');
@@ -121,7 +122,7 @@ describe('IDE: User opens IDE', () => {
 
   describe('path in URL is a directory', () => {
     beforeEach(async () => {
-      vm = ideHelper.createIdeComponent(container, { path: 'files/images' });
+      vm = startWebIDE(container, { path: 'files/images' });
 
       // wait for folders in left sidebar to be expanded
       await screen.findByText('images');
@@ -144,7 +145,7 @@ describe('IDE: User opens IDE', () => {
 
   describe("a file for path in url doesn't exist in the repo", () => {
     beforeEach(async () => {
-      vm = ideHelper.createIdeComponent(container, { path: 'abracadabra/hocus-focus.txt' });
+      vm = startWebIDE(container, { path: 'abracadabra/hocus-focus.txt' });
 
       // a new tab is open for hocus-focus.txt
       await findByText(document.querySelector('.multi-file-edit-pane'), 'hocus-focus.txt');
diff --git a/spec/frontend_integration/test_helpers/fixtures.js b/spec/frontend_integration/test_helpers/fixtures.js
index 294a0d7709146..46946ed71f2ad 100644
--- a/spec/frontend_integration/test_helpers/fixtures.js
+++ b/spec/frontend_integration/test_helpers/fixtures.js
@@ -1,24 +1,40 @@
 /* eslint-disable global-require, import/no-unresolved */
 import { memoize } from 'lodash';
-import { readFileSync } from 'fs';
-import { join } from 'path';
 
-export const getProject = () => require('test_fixtures/api/projects/get.json');
-export const getEmptyProject = () => require('test_fixtures/api/projects/get_empty.json');
-export const getBranch = () => require('test_fixtures/api/projects/branches/get.json');
-export const getMergeRequests = () => require('test_fixtures/api/merge_requests/get.json');
-export const getRepositoryFiles = () => require('test_fixtures/projects_json/files.json');
+const createFactoryWithDefault = (fn, defaultValue) => () => {
+  try {
+    return fn();
+  } catch {
+    return defaultValue;
+  }
+};
 
-export const getBlobReadme = () =>
-  readFileSync(require.resolve('test_fixtures/blob/text/README.md'), 'utf8');
-export const getBlobZip = () =>
-  readFileSync(require.resolve('test_fixtures/blob/binary/Gemfile.zip'), 'utf8');
-export const getBlobImage = () =>
-  readFileSync(
-    join(require.resolve('test_fixtures/blob/text/README.md'), '../..', 'images/logo-white.png'),
-    'utf8',
-  );
+const factory = {
+  json: fn => createFactoryWithDefault(fn, { error: 'fixture not found' }),
+  text: fn => createFactoryWithDefault(fn, 'Hello world\nHow are you today?\n'),
+  binary: fn => createFactoryWithDefault(fn, ''),
+};
 
-export const getPipelinesEmptyResponse = () =>
-  require('test_fixtures/projects_json/pipelines_empty.json');
+export const getProject = factory.json(() => require('test_fixtures/api/projects/get.json'));
+export const getEmptyProject = factory.json(() =>
+  require('test_fixtures/api/projects/get_empty.json'),
+);
+export const getBranch = factory.json(() =>
+  require('test_fixtures/api/projects/branches/get.json'),
+);
+export const getMergeRequests = factory.json(() =>
+  require('test_fixtures/api/merge_requests/get.json'),
+);
+export const getRepositoryFiles = factory.json(() =>
+  require('test_fixtures/projects_json/files.json'),
+);
+export const getPipelinesEmptyResponse = factory.json(() =>
+  require('test_fixtures/projects_json/pipelines_empty.json'),
+);
 export const getCommit = memoize(() => getBranch().commit);
+
+export const getBlobReadme = factory.text(() => require('test_fixtures/blob/text/README.md'));
+export const getBlobZip = factory.binary(() => require('test_fixtures/blob/binary/Gemfile.zip'));
+export const getBlobImage = factory.binary(() =>
+  require('test_fixtures/blob/images/logo-white.png'),
+);
-- 
GitLab