import { createStore } from '~/ide/stores'; import mutations from '~/ide/stores/mutations/file'; import { file } from '../../helpers'; describe('IDE store file mutations', () => { let localState; let localStore; let localFile; beforeEach(() => { localStore = createStore(); localState = localStore.state; localFile = { ...file('file'), type: 'blob', content: 'original' }; localState.entries[localFile.path] = localFile; }); describe('SET_FILE_ACTIVE', () => { it('sets the file active', () => { mutations.SET_FILE_ACTIVE(localState, { path: localFile.path, active: true, }); expect(localFile.active).toBe(true); }); it('sets pending tab as not active', () => { localState.openFiles.push({ ...localFile, pending: true, active: true }); mutations.SET_FILE_ACTIVE(localState, { path: localFile.path, active: true, }); expect(localState.openFiles[0].active).toBe(false); }); }); describe('TOGGLE_FILE_OPEN', () => { it('adds into opened files', () => { mutations.TOGGLE_FILE_OPEN(localState, localFile.path); expect(localFile.opened).toBe(true); expect(localState.openFiles.length).toBe(1); }); describe('if already open', () => { it('removes from opened files', () => { mutations.TOGGLE_FILE_OPEN(localState, localFile.path); mutations.TOGGLE_FILE_OPEN(localState, localFile.path); expect(localFile.opened).toBe(false); expect(localState.openFiles.length).toBe(0); }); }); it.each` entry | loading ${{ opened: false }} | ${true} ${{ opened: false, tempFile: true }} | ${false} ${{ opened: true }} | ${false} `('for state: $entry, sets loading=$loading', ({ entry, loading }) => { Object.assign(localFile, entry); mutations.TOGGLE_FILE_OPEN(localState, localFile.path); expect(localFile.loading).toBe(loading); }); }); describe('SET_FILE_DATA', () => { it('sets extra file data', () => { mutations.SET_FILE_DATA(localState, { data: { raw_path: 'raw', }, file: localFile, }); expect(localFile.rawPath).toBe('raw'); expect(localFile.raw).toBeNull(); expect(localFile.baseRaw).toBeNull(); }); it('sets extra file data to all arrays concerned', () => { localState.stagedFiles = [localFile]; localState.changedFiles = [localFile]; localState.openFiles = [localFile]; const rawPath = 'foo/bar/blah.md'; mutations.SET_FILE_DATA(localState, { data: { raw_path: rawPath, }, file: localFile, }); expect(localState.stagedFiles[0].rawPath).toEqual(rawPath); expect(localState.changedFiles[0].rawPath).toEqual(rawPath); expect(localState.openFiles[0].rawPath).toEqual(rawPath); expect(localFile.rawPath).toEqual(rawPath); }); it('does not mutate certain props on the file', () => { const path = 'New Path'; const name = 'New Name'; localFile.path = path; localFile.name = name; localState.stagedFiles = [localFile]; localState.changedFiles = [localFile]; localState.openFiles = [localFile]; mutations.SET_FILE_DATA(localState, { data: { path: 'Old Path', name: 'Old Name', raw: 'Old Raw', base_raw: 'Old Base Raw', }, file: localFile, }); [ localState.stagedFiles[0], localState.changedFiles[0], localState.openFiles[0], localFile, ].forEach((f) => { expect(f).toEqual( expect.objectContaining({ path, name, raw: null, baseRaw: null, }), ); }); }); }); describe('SET_FILE_RAW_DATA', () => { const callMutationForFile = (f) => { mutations.SET_FILE_RAW_DATA(localState, { file: f, raw: 'testing', fileDeletedAndReadded: localStore.getters.isFileDeletedAndReadded(localFile.path), }); }; it('sets raw data', () => { callMutationForFile(localFile); expect(localFile.raw).toBe('testing'); }); it('sets raw data to stagedFile if file was deleted and readded', () => { localState.stagedFiles = [{ ...localFile, deleted: true }]; localFile.tempFile = true; callMutationForFile(localFile); expect(localFile.raw).toEqual(''); expect(localState.stagedFiles[0].raw).toBe('testing'); }); it("sets raw data to a file's content if tempFile is empty", () => { localFile.tempFile = true; localFile.content = ''; callMutationForFile(localFile); expect(localFile.raw).toEqual(''); expect(localFile.content).toBe('testing'); }); it('adds raw data to open pending file', () => { localState.openFiles.push({ ...localFile, pending: true }); callMutationForFile(localFile); expect(localState.openFiles[0].raw).toBe('testing'); }); it('sets raw to content of a renamed tempFile', () => { localFile.tempFile = true; localFile.prevPath = 'old_path'; localState.openFiles.push({ ...localFile, pending: true }); callMutationForFile(localFile); expect(localState.openFiles[0].raw).not.toBe('testing'); expect(localState.openFiles[0].content).toBe('testing'); }); it('adds raw data to a staged deleted file if unstaged change has a tempFile of the same name', () => { localFile.tempFile = true; localState.openFiles.push({ ...localFile, pending: true }); localState.stagedFiles = [{ ...localFile, deleted: true }]; callMutationForFile(localFile); expect(localFile.raw).toEqual(''); expect(localState.stagedFiles[0].raw).toBe('testing'); }); }); describe('SET_FILE_BASE_RAW_DATA', () => { it('sets raw data from base branch', () => { mutations.SET_FILE_BASE_RAW_DATA(localState, { file: localFile, baseRaw: 'testing', }); expect(localFile.baseRaw).toBe('testing'); }); }); describe('UPDATE_FILE_CONTENT', () => { beforeEach(() => { localFile.raw = 'test'; }); it('sets content', () => { mutations.UPDATE_FILE_CONTENT(localState, { path: localFile.path, content: 'test', }); expect(localFile.content).toBe('test'); }); it('sets changed if content does not match raw', () => { mutations.UPDATE_FILE_CONTENT(localState, { path: localFile.path, content: 'testing', }); expect(localFile.content).toBe('testing'); expect(localFile.changed).toBe(true); }); it('sets changed if file is a temp file', () => { localFile.tempFile = true; mutations.UPDATE_FILE_CONTENT(localState, { path: localFile.path, content: '', }); expect(localFile.changed).toBe(true); }); }); describe('SET_FILE_MERGE_REQUEST_CHANGE', () => { it('sets file mr change', () => { mutations.SET_FILE_MERGE_REQUEST_CHANGE(localState, { file: localFile, mrChange: { diff: 'ABC', }, }); expect(localFile.mrChange.diff).toBe('ABC'); }); it('has diffMode replaced by default', () => { mutations.SET_FILE_MERGE_REQUEST_CHANGE(localState, { file: localFile, mrChange: { diff: 'ABC', }, }); expect(localFile.mrChange.diffMode).toBe('replaced'); }); it('has diffMode new', () => { mutations.SET_FILE_MERGE_REQUEST_CHANGE(localState, { file: localFile, mrChange: { diff: 'ABC', new_file: true, }, }); expect(localFile.mrChange.diffMode).toBe('new'); }); it('has diffMode deleted', () => { mutations.SET_FILE_MERGE_REQUEST_CHANGE(localState, { file: localFile, mrChange: { diff: 'ABC', deleted_file: true, }, }); expect(localFile.mrChange.diffMode).toBe('deleted'); }); it('has diffMode renamed', () => { mutations.SET_FILE_MERGE_REQUEST_CHANGE(localState, { file: localFile, mrChange: { diff: 'ABC', renamed_file: true, }, }); expect(localFile.mrChange.diffMode).toBe('renamed'); }); }); describe('DISCARD_FILE_CHANGES', () => { beforeEach(() => { localFile.content = 'test'; localFile.changed = true; localState.currentProjectId = 'gitlab-ce'; localState.currentBranchId = 'main'; localState.trees['gitlab-ce/main'] = { tree: [], }; }); it('resets content and changed', () => { mutations.DISCARD_FILE_CHANGES(localState, localFile.path); expect(localFile.content).toBe(''); expect(localFile.changed).toBe(false); }); it('adds to root tree if deleted', () => { localFile.deleted = true; mutations.DISCARD_FILE_CHANGES(localState, localFile.path); expect(localState.trees['gitlab-ce/main'].tree).toEqual([{ ...localFile, deleted: false }]); }); it('adds to parent tree if deleted', () => { localFile.deleted = true; localFile.parentPath = 'parentPath'; localState.entries.parentPath = { tree: [], }; mutations.DISCARD_FILE_CHANGES(localState, localFile.path); expect(localState.entries.parentPath.tree).toEqual([{ ...localFile, deleted: false }]); }); }); describe('ADD_FILE_TO_CHANGED', () => { it('adds file into changed files array', () => { mutations.ADD_FILE_TO_CHANGED(localState, localFile.path); expect(localState.changedFiles.length).toBe(1); }); }); describe('REMOVE_FILE_FROM_CHANGED', () => { it('removes files from changed files array', () => { localState.changedFiles.push(localFile); mutations.REMOVE_FILE_FROM_CHANGED(localState, localFile.path); expect(localState.changedFiles.length).toBe(0); }); }); describe.each` mutationName | mutation | addedTo | removedFrom | staged | changedFilesCount | stagedFilesCount ${'STAGE_CHANGE'} | ${mutations.STAGE_CHANGE} | ${'stagedFiles'} | ${'changedFiles'} | ${true} | ${0} | ${1} ${'UNSTAGE_CHANGE'} | ${mutations.UNSTAGE_CHANGE} | ${'changedFiles'} | ${'stagedFiles'} | ${false} | ${1} | ${0} `( '$mutationName', ({ mutation, changedFilesCount, removedFrom, addedTo, staged, stagedFilesCount }) => { let unstagedFile; let stagedFile; beforeEach(() => { unstagedFile = { ...file('file'), type: 'blob', raw: 'original content', content: 'changed content', }; stagedFile = { ...unstagedFile, content: 'staged content', staged: true, }; localState.changedFiles.push(unstagedFile); localState.stagedFiles.push(stagedFile); localState.entries[unstagedFile.path] = unstagedFile; }); it('removes all changes of a file if staged and unstaged change contents are equal', () => { unstagedFile.content = 'original content'; mutation(localState, { path: unstagedFile.path, diffInfo: localStore.getters.getDiffInfo(unstagedFile.path), }); expect(localState.entries.file).toEqual( expect.objectContaining({ content: 'original content', staged: false, changed: false, }), ); expect(localState.stagedFiles.length).toBe(0); expect(localState.changedFiles.length).toBe(0); }); it('removes all changes of a file if a file is deleted and a new file with same content is added', () => { stagedFile.deleted = true; unstagedFile.tempFile = true; unstagedFile.content = 'original content'; mutation(localState, { path: unstagedFile.path, diffInfo: localStore.getters.getDiffInfo(unstagedFile.path), }); expect(localState.stagedFiles.length).toBe(0); expect(localState.changedFiles.length).toBe(0); expect(localState.entries.file).toEqual( expect.objectContaining({ content: 'original content', deleted: false, tempFile: false, }), ); }); it('merges deleted and added file into a changed file if the contents differ', () => { stagedFile.deleted = true; unstagedFile.tempFile = true; unstagedFile.content = 'hello'; mutation(localState, { path: unstagedFile.path, diffInfo: localStore.getters.getDiffInfo(unstagedFile.path), }); expect(localState.stagedFiles.length).toBe(stagedFilesCount); expect(localState.changedFiles.length).toBe(changedFilesCount); expect(unstagedFile).toEqual( expect.objectContaining({ content: 'hello', staged, deleted: false, tempFile: false, changed: true, }), ); }); it('does not remove file from stagedFiles and changedFiles if the file was renamed, even if the contents are equal', () => { unstagedFile.content = 'original content'; unstagedFile.prevPath = 'old_file'; mutation(localState, { path: unstagedFile.path, diffInfo: localStore.getters.getDiffInfo(unstagedFile.path), }); expect(localState.entries.file).toEqual( expect.objectContaining({ content: 'original content', staged, changed: false, prevPath: 'old_file', }), ); expect(localState.stagedFiles.length).toBe(stagedFilesCount); expect(localState.changedFiles.length).toBe(changedFilesCount); }); it(`removes file from ${removedFrom} array and adds it into ${addedTo} array`, () => { localState.stagedFiles.length = 0; mutation(localState, { path: unstagedFile.path, diffInfo: localStore.getters.getDiffInfo(unstagedFile.path), }); expect(localState.stagedFiles.length).toBe(stagedFilesCount); expect(localState.changedFiles.length).toBe(changedFilesCount); const f = localState.stagedFiles[0] || localState.changedFiles[0]; expect(f).toEqual(unstagedFile); }); it(`updates file in ${addedTo} array if it is was already present in it`, () => { unstagedFile.raw = 'testing 123'; mutation(localState, { path: unstagedFile.path, diffInfo: localStore.getters.getDiffInfo(unstagedFile.path), }); expect(localState.stagedFiles.length).toBe(stagedFilesCount); expect(localState.changedFiles.length).toBe(changedFilesCount); const f = localState.stagedFiles[0] || localState.changedFiles[0]; expect(f.raw).toEqual('testing 123'); }); }, ); describe('TOGGLE_FILE_CHANGED', () => { it('updates file changed status', () => { mutations.TOGGLE_FILE_CHANGED(localState, { file: localFile, changed: true, }); expect(localFile.changed).toBe(true); }); }); describe('ADD_PENDING_TAB', () => { beforeEach(() => { const f = { ...file('openFile'), path: 'openFile', active: true, opened: true }; localState.entries[f.path] = f; localState.openFiles.push(f); }); it('adds file into openFiles as pending', () => { mutations.ADD_PENDING_TAB(localState, { file: localFile, }); expect(localState.openFiles.length).toBe(1); expect(localState.openFiles[0].pending).toBe(true); expect(localState.openFiles[0].key).toBe(`pending-${localFile.key}`); }); it('only allows 1 open pending file', () => { const newFile = file('test'); localState.entries[newFile.path] = newFile; mutations.ADD_PENDING_TAB(localState, { file: localFile, }); expect(localState.openFiles.length).toBe(1); mutations.ADD_PENDING_TAB(localState, { file: file('test'), }); expect(localState.openFiles.length).toBe(1); expect(localState.openFiles[0].name).toBe('test'); }); }); describe('REMOVE_PENDING_TAB', () => { it('removes pending tab from openFiles', () => { localFile.key = 'testing'; localState.openFiles.push(localFile); mutations.REMOVE_PENDING_TAB(localState, localFile); expect(localState.openFiles.length).toBe(0); }); }); });