diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-12-17 11:59:07 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-12-17 11:59:07 +0000 |
commit | 8b573c94895dc0ac0e1d9d59cf3e8745e8b539ca (patch) | |
tree | 544930fb309b30317ae9797a9683768705d664c4 /spec/frontend_integration | |
parent | 4b1de649d0168371549608993deac953eb692019 (diff) | |
download | gitlab-ce-8b573c94895dc0ac0e1d9d59cf3e8745e8b539ca.tar.gz |
Add latest changes from gitlab-org/gitlab@13-7-stable-eev13.7.0-rc42
Diffstat (limited to 'spec/frontend_integration')
-rw-r--r-- | spec/frontend_integration/.eslintrc.yml | 4 | ||||
-rw-r--r-- | spec/frontend_integration/ide/__snapshots__/ide_integration_spec.js.snap | 114 | ||||
-rw-r--r-- | spec/frontend_integration/ide/helpers/ide_helper.js (renamed from spec/frontend_integration/ide/ide_helper.js) | 71 | ||||
-rw-r--r-- | spec/frontend_integration/ide/helpers/mock_data.js | 12 | ||||
-rw-r--r-- | spec/frontend_integration/ide/helpers/start.js | 17 | ||||
-rw-r--r-- | spec/frontend_integration/ide/ide_integration_spec.js | 136 | ||||
-rw-r--r-- | spec/frontend_integration/ide/user_opens_file_spec.js | 89 | ||||
-rw-r--r-- | spec/frontend_integration/ide/user_opens_ide_spec.js | 160 | ||||
-rw-r--r-- | spec/frontend_integration/test_helpers/fixtures.js | 42 | ||||
-rw-r--r-- | spec/frontend_integration/test_helpers/mock_server/index.js | 27 | ||||
-rw-r--r-- | spec/frontend_integration/test_helpers/mock_server/routes/repository.js | 12 |
11 files changed, 512 insertions, 172 deletions
diff --git a/spec/frontend_integration/.eslintrc.yml b/spec/frontend_integration/.eslintrc.yml index 2460e218f59..8fff491bdcf 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/__snapshots__/ide_integration_spec.js.snap b/spec/frontend_integration/ide/__snapshots__/ide_integration_spec.js.snap deleted file mode 100644 index 877cc78a111..00000000000 --- a/spec/frontend_integration/ide/__snapshots__/ide_integration_spec.js.snap +++ /dev/null @@ -1,114 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`WebIDE runs 1`] = ` -<div> - <article - class="ide position-relative d-flex flex-column align-items-stretch" - > - <div - class="ide-view flex-grow d-flex" - > - <div - class="gl-relative multi-file-commit-panel flex-column" - style="width: 340px;" - > - <div - class="multi-file-commit-panel-inner" - data-testid="ide-side-bar-inner" - > - <div - class="multi-file-loading-container" - > - <div - class="animation-container" - > - <div - class="skeleton-line-1" - /> - <div - class="skeleton-line-2" - /> - <div - class="skeleton-line-3" - /> - </div> - </div> - <div - class="multi-file-loading-container" - > - <div - class="animation-container" - > - <div - class="skeleton-line-1" - /> - <div - class="skeleton-line-2" - /> - <div - class="skeleton-line-3" - /> - </div> - </div> - <div - class="multi-file-loading-container" - > - <div - class="animation-container" - > - <div - class="skeleton-line-1" - /> - <div - class="skeleton-line-2" - /> - <div - class="skeleton-line-3" - /> - </div> - </div> - </div> - <div - class="position-absolute position-top-0 position-bottom-0 drag-handle position-right-0" - size="340" - style="cursor: ew-resize;" - /> - </div> - <div - class="multi-file-edit-pane" - > - <div - class="ide-empty-state" - > - <div - class="row js-empty-state" - > - <div - class="col-12" - > - <div - class="svg-content svg-250" - > - <img - src="/test/empty_state.svg" - /> - </div> - </div> - <div - class="col-12" - > - <div - class="text-content text-center" - > - <h4> - Make and review changes in the browser with the Web IDE - </h4> - </div> - </div> - </div> - </div> - </div> - </div> - </article> -</div> -`; diff --git a/spec/frontend_integration/ide/ide_helper.js b/spec/frontend_integration/ide/helpers/ide_helper.js index fea8bc24031..fe8d5f93794 100644 --- a/spec/frontend_integration/ide/ide_helper.js +++ b/spec/frontend_integration/ide/helpers/ide_helper.js @@ -1,10 +1,18 @@ -import { findAllByText, fireEvent, getByLabelText, screen } from '@testing-library/dom'; +import { + findAllByText, + fireEvent, + getByLabelText, + findByTestId, + getByText, + screen, + findByText, +} from '@testing-library/dom'; const isFolderRowOpen = row => row.matches('.folder.is-open'); const getLeftSidebar = () => screen.getByTestId('left-sidebar'); -const clickOnLeftSidebarTab = name => { +export const switchLeftSidebarTab = name => { const sidebar = getLeftSidebar(); const button = getByLabelText(sidebar, name); @@ -12,16 +20,31 @@ const clickOnLeftSidebarTab = name => { button.click(); }; -const findMonacoEditor = () => - screen.findByLabelText(/Editor content;/).then(x => x.closest('.monaco-editor')); +export const getStatusBar = () => document.querySelector('.ide-status-bar'); -const findAndSetEditorValue = async value => { +export const waitForMonacoEditor = () => + new Promise(resolve => window.monaco.editor.onDidCreateEditor(resolve)); + +export const findMonacoEditor = () => + screen.findAllByLabelText(/Editor content;/).then(([x]) => x.closest('.monaco-editor')); + +export const findMonacoDiffEditor = () => + screen.findAllByLabelText(/Editor content;/).then(([x]) => x.closest('.monaco-diff-editor')); + +export const findAndSetEditorValue = async value => { const editor = await findMonacoEditor(); const uri = editor.getAttribute('data-uri'); window.monaco.editor.getModel(uri).setValue(value); }; +export const getEditorValue = async () => { + const editor = await findMonacoEditor(); + const uri = editor.getAttribute('data-uri'); + + return window.monaco.editor.getModel(uri).getValue(); +}; + const findTreeBody = () => screen.findByTestId('ide-tree-body', {}, { timeout: 5000 }); const findRootActions = () => screen.findByTestId('ide-root-actions', {}, { timeout: 7000 }); @@ -68,11 +91,13 @@ const clickFileRowAction = (row, name) => { dropdownAction.click(); }; -const findAndSetFileName = async value => { - const nameField = await screen.findByTestId('file-name-field'); +const fillFileNameModal = async (value, submitText = 'Create file') => { + const modal = await screen.findByTestId('ide-new-entry'); + + const nameField = await findByTestId(modal, 'file-name-field'); fireEvent.input(nameField, { target: { value } }); - const createButton = screen.getByText('Create file'); + const createButton = getByText(modal, submitText, { selector: 'button' }); createButton.click(); }; @@ -83,12 +108,19 @@ const findAndClickRootAction = async name => { button.click(); }; +export const clickPreviewMarkdown = () => { + screen.getByText('Preview Markdown').click(); +}; + export const openFile = async path => { const row = await findAndTraverseToPath(path); openFileRow(row); }; +export const waitForTabToOpen = fileName => + findByText(document.querySelector('.multi-file-edit-pane'), fileName); + export const createFile = async (path, content) => { const parentPath = path .split('/') @@ -103,17 +135,36 @@ export const createFile = async (path, content) => { await findAndClickRootAction('New file'); } - await findAndSetFileName(path); + await fillFileNameModal(path); await findAndSetEditorValue(content); }; +export const getFilesList = () => { + return screen.getAllByTestId('file-row-name-container').map(e => e.textContent.trim()); +}; + export const deleteFile = async path => { const row = await findAndTraverseToPath(path); clickFileRowAction(row, 'Delete'); }; +export const renameFile = async (path, newPath) => { + const row = await findAndTraverseToPath(path); + clickFileRowAction(row, 'Rename/Move'); + + await fillFileNameModal(newPath, 'Rename file'); +}; + +export const closeFile = async path => { + const button = await screen.getByLabelText(`Close ${path}`, { + selector: '.multi-file-tabs button', + }); + + button.click(); +}; + export const commit = async () => { - clickOnLeftSidebarTab('Commit'); + switchLeftSidebarTab('Commit'); screen.getByTestId('begin-commit-button').click(); await screen.findByLabelText(/Commit to .+ branch/).then(x => x.click()); diff --git a/spec/frontend_integration/ide/helpers/mock_data.js b/spec/frontend_integration/ide/helpers/mock_data.js new file mode 100644 index 00000000000..f70739e5ac0 --- /dev/null +++ b/spec/frontend_integration/ide/helpers/mock_data.js @@ -0,0 +1,12 @@ +export const IDE_DATASET = { + emptyStateSvgPath: '/test/empty_state.svg', + noChangesStateSvgPath: '/test/no_changes_state.svg', + committedStateSvgPath: '/test/committed_state.svg', + pipelinesEmptyStateSvgPath: '/test/pipelines_empty_state.svg', + promotionSvgPath: '/test/promotion.svg', + ciHelpPagePath: '/test/ci_help_page', + webIDEHelpPagePath: '/test/web_ide_help_page', + clientsidePreviewEnabled: 'true', + renderWhitespaceInCode: 'false', + codesandboxBundlerUrl: 'test/codesandbox_bundler', +}; diff --git a/spec/frontend_integration/ide/helpers/start.js b/spec/frontend_integration/ide/helpers/start.js new file mode 100644 index 00000000000..9dc9649e1bf --- /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 1f5c1d38450..dacc538d5ba 100644 --- a/spec/frontend_integration/ide/ide_integration_spec.js +++ b/spec/frontend_integration/ide/ide_integration_spec.js @@ -1,61 +1,28 @@ -import { TEST_HOST } from 'helpers/test_constants'; import { waitForText } from 'helpers/wait_for_text'; import waitForPromises from 'helpers/wait_for_promises'; import { useOverclockTimers } from 'test_helpers/utils/overclock_timers'; import { createCommitId } from 'test_helpers/factories/commit_id'; -import { initIde } from '~/ide'; -import extendStore from '~/ide/stores/extend'; -import * as ideHelper from './ide_helper'; - -const TEST_DATASET = { - emptyStateSvgPath: '/test/empty_state.svg', - noChangesStateSvgPath: '/test/no_changes_state.svg', - committedStateSvgPath: '/test/committed_state.svg', - pipelinesEmptyStateSvgPath: '/test/pipelines_empty_state.svg', - promotionSvgPath: '/test/promotion.svg', - ciHelpPagePath: '/test/ci_help_page', - webIDEHelpPagePath: '/test/web_ide_help_page', - clientsidePreviewEnabled: 'true', - renderWhitespaceInCode: 'false', - codesandboxBundlerUrl: 'test/codesandbox_bundler', -}; +import * as ideHelper from './helpers/ide_helper'; +import startWebIDE from './helpers/start'; describe('WebIDE', () => { useOverclockTimers(); let vm; - let root; + let container; beforeEach(() => { - root = document.createElement('div'); - document.body.appendChild(root); - - global.jsdom.reconfigure({ - url: `${TEST_HOST}/-/ide/project/gitlab-test/lorem-ipsum`, - }); + setFixtures('<div class="webide-container"></div>'); + container = document.querySelector('.webide-container'); }); afterEach(() => { vm.$destroy(); vm = null; - root.remove(); - }); - - const createComponent = () => { - const el = document.createElement('div'); - Object.assign(el.dataset, TEST_DATASET); - root.appendChild(el); - vm = initIde(el, { extendStore }); - }; - - it('runs', () => { - createComponent(); - - expect(root).toMatchSnapshot(); }); it('user commits changes', async () => { - createComponent(); + vm = startWebIDE(container); await ideHelper.createFile('foo/bar/test.txt', 'Lorem ipsum dolar sit'); await ideHelper.deleteFile('foo/bar/.gitkeep'); @@ -89,7 +56,7 @@ describe('WebIDE', () => { }); it('user adds file that starts with +', async () => { - createComponent(); + vm = startWebIDE(container); await ideHelper.createFile('+test', 'Hello world!'); await ideHelper.openFile('+test'); @@ -101,4 +68,93 @@ describe('WebIDE', () => { const tabs = Array.from(document.querySelectorAll('.multi-file-tab')); expect(tabs.map(x => x.textContent.trim())).toEqual(['+test']); }); + + describe('editor info', () => { + let statusBar; + let editor; + + const waitForEditor = async () => { + editor = await ideHelper.waitForMonacoEditor(); + }; + + const changeEditorPosition = async (lineNumber, column) => { + editor.setPosition({ lineNumber, column }); + + await vm.$nextTick(); + }; + + beforeEach(async () => { + vm = startWebIDE(container); + + await ideHelper.openFile('README.md'); + editor = await ideHelper.waitForMonacoEditor(); + + statusBar = ideHelper.getStatusBar(); + }); + + it('shows line position and type', () => { + expect(statusBar).toHaveText('1:1'); + expect(statusBar).toHaveText('markdown'); + }); + + it('persists viewer', async () => { + const markdownPreview = 'test preview_markdown result'; + mockServer.post('/:namespace/:project/preview_markdown', () => ({ + body: markdownPreview, + })); + + await ideHelper.openFile('README.md'); + ideHelper.clickPreviewMarkdown(); + + const el = await waitForText(markdownPreview); + expect(el).toHaveText(markdownPreview); + + // Need to wait for monaco editor to load so it doesn't through errors on dispose + await ideHelper.openFile('.gitignore'); + await ideHelper.waitForMonacoEditor(); + await ideHelper.openFile('README.md'); + await ideHelper.waitForMonacoEditor(); + + expect(el).toHaveText(markdownPreview); + }); + + describe('when editor position changes', () => { + beforeEach(async () => { + await changeEditorPosition(4, 10); + }); + + it('shows new line position', () => { + expect(statusBar).not.toHaveText('1:1'); + expect(statusBar).toHaveText('4:10'); + }); + + it('updates after rename', async () => { + await ideHelper.renameFile('README.md', 'READMEZ.txt'); + await waitForEditor(); + + expect(statusBar).toHaveText('1:1'); + expect(statusBar).toHaveText('plaintext'); + }); + + it('persists position after opening then rename', async () => { + await ideHelper.openFile('files/js/application.js'); + await waitForEditor(); + await ideHelper.renameFile('README.md', 'READING_RAINBOW.md'); + await ideHelper.openFile('READING_RAINBOW.md'); + await waitForEditor(); + + expect(statusBar).toHaveText('4:10'); + expect(statusBar).toHaveText('markdown'); + }); + + it('persists position after closing', async () => { + await ideHelper.closeFile('README.md'); + await ideHelper.openFile('README.md'); + await waitForEditor(); + + expect(statusBar).toHaveText('4:10'); + expect(statusBar).toHaveText('markdown'); + }); + }); + }); }); diff --git a/spec/frontend_integration/ide/user_opens_file_spec.js b/spec/frontend_integration/ide/user_opens_file_spec.js new file mode 100644 index 00000000000..98a73c7a029 --- /dev/null +++ b/spec/frontend_integration/ide/user_opens_file_spec.js @@ -0,0 +1,89 @@ +import { useOverclockTimers } from 'test_helpers/utils/overclock_timers'; +import { screen } from '@testing-library/dom'; +import * as ideHelper from './helpers/ide_helper'; +import startWebIDE from './helpers/start'; + +// https://gitlab.com/gitlab-org/gitlab/-/issues/293654#note_466432769 +// eslint-disable-next-line jest/no-disabled-tests +describe.skip('IDE: User opens a file in the Web IDE', () => { + useOverclockTimers(); + + let vm; + let container; + + beforeEach(async () => { + setFixtures('<div class="webide-container"></div>'); + container = document.querySelector('.webide-container'); + + vm = startWebIDE(container); + + await screen.findByText('README'); // wait for file tree to load + }); + + afterEach(() => { + vm.$destroy(); + vm = null; + }); + + describe('user opens a directory', () => { + beforeEach(async () => { + await ideHelper.openFile('files/images'); + await screen.findByText('logo-white.png'); + }); + + it('expands directory in the left sidebar', () => { + expect(ideHelper.getFilesList()).toEqual( + expect.arrayContaining(['html', 'js', 'images', 'logo-white.png']), + ); + }); + }); + + describe('user opens a text file', () => { + beforeEach(async () => { + await ideHelper.openFile('README.md'); + await ideHelper.waitForTabToOpen('README.md'); + }); + + it('opens the file in monaco editor', async () => { + expect(await ideHelper.getEditorValue()).toContain('Sample repo for testing gitlab features'); + }); + + describe('user switches to review mode', () => { + beforeEach(() => { + ideHelper.switchLeftSidebarTab('Review'); + }); + + it('shows diff editor', async () => { + expect(await ideHelper.findMonacoDiffEditor()).toBeDefined(); + }); + }); + }); + + describe('user opens an image file', () => { + beforeEach(async () => { + await ideHelper.openFile('files/images/logo-white.png'); + await ideHelper.waitForTabToOpen('logo-white.png'); + }); + + it('opens image viewer for the file', async () => { + const viewer = await screen.findByTestId('image-viewer'); + const img = viewer.querySelector('img'); + + expect(img.src).toContain('logo-white.png'); + }); + }); + + describe('user opens a binary file', () => { + beforeEach(async () => { + await ideHelper.openFile('Gemfile.zip'); + await ideHelper.waitForTabToOpen('Gemfile.zip'); + }); + + it('opens image viewer for the file', async () => { + const downloadButton = await screen.findByText('Download'); + + expect(downloadButton.getAttribute('download')).toEqual('Gemfile.zip'); + expect(downloadButton.getAttribute('href')).toContain('/raw/'); + }); + }); +}); diff --git a/spec/frontend_integration/ide/user_opens_ide_spec.js b/spec/frontend_integration/ide/user_opens_ide_spec.js new file mode 100644 index 00000000000..502cb2e2c7d --- /dev/null +++ b/spec/frontend_integration/ide/user_opens_ide_spec.js @@ -0,0 +1,160 @@ +import { useOverclockTimers } from 'test_helpers/utils/overclock_timers'; +import { screen } from '@testing-library/dom'; +import * as ideHelper from './helpers/ide_helper'; +import startWebIDE from './helpers/start'; + +describe('IDE: User opens IDE', () => { + useOverclockTimers(); + + let vm; + let container; + + beforeEach(() => { + setFixtures('<div class="webide-container"></div>'); + container = document.querySelector('.webide-container'); + }); + + afterEach(() => { + vm.$destroy(); + vm = null; + }); + + it('shows loading indicator while the IDE is loading', async () => { + vm = startWebIDE(container); + + expect(container.querySelectorAll('.multi-file-loading-container')).toHaveLength(3); + }); + + describe('when the project is empty', () => { + beforeEach(() => { + vm = startWebIDE(container, { isRepoEmpty: true }); + }); + + it('shows "No files" in the left sidebar', async () => { + expect(await screen.findByText('No files')).toBeDefined(); + }); + + it('shows a "New file" button', async () => { + const button = await screen.findByTitle('New file'); + + expect(button.tagName).toEqual('BUTTON'); + }); + }); + + describe('when the file tree is loaded', () => { + beforeEach(async () => { + vm = startWebIDE(container); + + await screen.findByText('README'); // wait for file tree to load + }); + + it('shows a list of files in the left sidebar', async () => { + expect(ideHelper.getFilesList()).toEqual( + expect.arrayContaining(['README', 'LICENSE', 'CONTRIBUTING.md']), + ); + }); + + it('shows empty state in the main editor window', async () => { + expect( + await screen.findByText( + "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes.", + ), + ).toBeDefined(); + }); + + it('shows commit button in disabled state', async () => { + const button = await screen.findByTestId('begin-commit-button'); + + expect(button.getAttribute('disabled')).toBeDefined(); + }); + + it('shows branch/MR dropdown with master selected', async () => { + const dropdown = await screen.findByTestId('ide-nav-dropdown'); + + expect(dropdown.textContent).toContain('master'); + }); + }); + + describe('a path to a text file is present in the URL', () => { + beforeEach(async () => { + vm = startWebIDE(container, { path: 'README.md' }); + + await ideHelper.waitForTabToOpen('README.md'); + }); + + it('opens the file and its contents are shown in Monaco', async () => { + expect(await ideHelper.getEditorValue()).toContain('Sample repo for testing gitlab features'); + }); + }); + + describe('a path to a binary file is present in the URL', () => { + beforeEach(async () => { + vm = startWebIDE(container, { path: 'Gemfile.zip' }); + + await ideHelper.waitForTabToOpen('Gemfile.zip'); + }); + + it('shows download viewer', async () => { + const downloadButton = await screen.findByText('Download'); + + expect(downloadButton.getAttribute('download')).toEqual('Gemfile.zip'); + expect(downloadButton.getAttribute('href')).toContain('/raw/'); + }); + }); + + describe('a path to an image is present in the URL', () => { + beforeEach(async () => { + vm = startWebIDE(container, { path: 'files/images/logo-white.png' }); + + await ideHelper.waitForTabToOpen('logo-white.png'); + }); + + it('shows image viewer', async () => { + const viewer = await screen.findByTestId('image-viewer'); + const img = viewer.querySelector('img'); + + expect(img.src).toContain('logo-white.png'); + }); + }); + + describe('path in URL is a directory', () => { + beforeEach(async () => { + vm = startWebIDE(container, { path: 'files/images' }); + + // wait for folders in left sidebar to be expanded + await screen.findByText('images'); + }); + + it('expands folders in the left sidebar', () => { + expect(ideHelper.getFilesList()).toEqual( + expect.arrayContaining(['files', 'images', 'logo-white.png', 'logo-black.png']), + ); + }); + + it('shows empty state in the main editor window', async () => { + expect( + await screen.findByText( + "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes.", + ), + ).toBeDefined(); + }); + }); + + describe("a file for path in url doesn't exist in the repo", () => { + beforeEach(async () => { + vm = startWebIDE(container, { path: 'abracadabra/hocus-focus.txt' }); + + await ideHelper.waitForTabToOpen('hocus-focus.txt'); + }); + + it('create new folders and file in the left sidebar', () => { + expect(ideHelper.getFilesList()).toEqual( + expect.arrayContaining(['abracadabra', 'hocus-focus.txt']), + ); + }); + + it('creates a blank new file', async () => { + expect(await ideHelper.getEditorValue()).toEqual('\n'); + }); + }); +}); diff --git a/spec/frontend_integration/test_helpers/fixtures.js b/spec/frontend_integration/test_helpers/fixtures.js index 50fa4859dfa..46946ed71f2 100644 --- a/spec/frontend_integration/test_helpers/fixtures.js +++ b/spec/frontend_integration/test_helpers/fixtures.js @@ -1,10 +1,40 @@ /* eslint-disable global-require, import/no-unresolved */ import { memoize } from 'lodash'; -export const getProject = () => require('test_fixtures/api/projects/get.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'); -export const getPipelinesEmptyResponse = () => - require('test_fixtures/projects_json/pipelines_empty.json'); +const createFactoryWithDefault = (fn, defaultValue) => () => { + try { + return fn(); + } catch { + return defaultValue; + } +}; + +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 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'), +); diff --git a/spec/frontend_integration/test_helpers/mock_server/index.js b/spec/frontend_integration/test_helpers/mock_server/index.js index b3979d05ea5..6f090565635 100644 --- a/spec/frontend_integration/test_helpers/mock_server/index.js +++ b/spec/frontend_integration/test_helpers/mock_server/index.js @@ -1,5 +1,14 @@ import { Server, Model, RestSerializer } from 'miragejs'; -import { getProject, getBranch, getMergeRequests, getRepositoryFiles } from 'test_helpers/fixtures'; +import { + getProject, + getEmptyProject, + getBranch, + getMergeRequests, + getRepositoryFiles, + getBlobReadme, + getBlobImage, + getBlobZip, +} from 'test_helpers/fixtures'; import setupRoutes from './routes'; export const createMockServerOptions = () => ({ @@ -18,9 +27,23 @@ export const createMockServerOptions = () => ({ seeds(schema) { schema.db.loadData({ files: getRepositoryFiles().map(path => ({ path })), - projects: [getProject()], + projects: [getProject(), getEmptyProject()], branches: [getBranch()], mergeRequests: getMergeRequests(), + filesRaw: [ + { + raw: getBlobReadme(), + path: 'README.md', + }, + { + raw: getBlobZip(), + path: 'Gemfile.zip', + }, + { + raw: getBlobImage(), + path: 'files/images/logo-white.png', + }, + ], userPermissions: [ { createMergeRequestIn: true, diff --git a/spec/frontend_integration/test_helpers/mock_server/routes/repository.js b/spec/frontend_integration/test_helpers/mock_server/routes/repository.js index c5e91c9e87e..166c0cc32db 100644 --- a/spec/frontend_integration/test_helpers/mock_server/routes/repository.js +++ b/spec/frontend_integration/test_helpers/mock_server/routes/repository.js @@ -19,6 +19,18 @@ export default server => { return schema.db.files.map(({ path }) => path); }); + server.get('/:namespace/:project/-/blob/:sha/*path', (schema, request) => { + const { path } = schema.db.files.findBy({ path: request.params.path }); + + return { path, rawPath: request.url.replace('/-/blob', '/-/raw') }; + }); + + server.get('/:namespace/:project/-/raw/:sha/*path', (schema, request) => { + const { path } = request.params; + + return schema.db.filesRaw.findBy({ path })?.raw || 'Sample content'; + }); + server.post('/api/v4/projects/:id/repository/commits', (schema, request) => { const { branch: branchName, commit_message: message, actions } = JSON.parse( request.requestBody, |