Commit 68236049 authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent 2f26f81c
...@@ -134,28 +134,40 @@ export const scrollToTab = () => { ...@@ -134,28 +134,40 @@ export const scrollToTab = () => {
}); });
}; };
export const stageAllChanges = ({ state, commit, dispatch }) => { export const stageAllChanges = ({ state, commit, dispatch, getters }) => {
const openFile = state.openFiles[0]; const openFile = state.openFiles[0];
commit(types.SET_LAST_COMMIT_MSG, ''); commit(types.SET_LAST_COMMIT_MSG, '');
state.changedFiles.forEach(file => commit(types.STAGE_CHANGE, file.path)); state.changedFiles.forEach(file =>
commit(types.STAGE_CHANGE, { path: file.path, diffInfo: getters.getDiffInfo(file.path) }),
);
dispatch('openPendingTab', { const file = getters.getStagedFile(openFile.path);
file: state.stagedFiles.find(f => f.path === openFile.path),
keyPrefix: stageKeys.staged, if (file) {
}); dispatch('openPendingTab', {
file,
keyPrefix: stageKeys.staged,
});
}
}; };
export const unstageAllChanges = ({ state, commit, dispatch }) => { export const unstageAllChanges = ({ state, commit, dispatch, getters }) => {
const openFile = state.openFiles[0]; const openFile = state.openFiles[0];
state.stagedFiles.forEach(file => commit(types.UNSTAGE_CHANGE, file.path)); state.stagedFiles.forEach(file =>
commit(types.UNSTAGE_CHANGE, { path: file.path, diffInfo: getters.getDiffInfo(file.path) }),
);
dispatch('openPendingTab', { const file = getters.getChangedFile(openFile.path);
file: state.changedFiles.find(f => f.path === openFile.path),
keyPrefix: stageKeys.unstaged, if (file) {
}); dispatch('openPendingTab', {
file,
keyPrefix: stageKeys.unstaged,
});
}
}; };
export const updateViewer = ({ commit }, viewer) => { export const updateViewer = ({ commit }, viewer) => {
......
...@@ -214,20 +214,20 @@ export const discardFileChanges = ({ dispatch, state, commit, getters }, path) = ...@@ -214,20 +214,20 @@ export const discardFileChanges = ({ dispatch, state, commit, getters }, path) =
eventHub.$emit(`editor.update.model.dispose.unstaged-${file.key}`, file.content); eventHub.$emit(`editor.update.model.dispose.unstaged-${file.key}`, file.content);
}; };
export const stageChange = ({ commit, state, dispatch }, path) => { export const stageChange = ({ commit, dispatch, getters }, path) => {
const stagedFile = state.stagedFiles.find(f => f.path === path); const stagedFile = getters.getStagedFile(path);
const openFile = state.openFiles.find(f => f.path === path); const openFile = getters.getOpenFile(path);
commit(types.STAGE_CHANGE, path); commit(types.STAGE_CHANGE, { path, diffInfo: getters.getDiffInfo(path) });
commit(types.SET_LAST_COMMIT_MSG, ''); commit(types.SET_LAST_COMMIT_MSG, '');
if (stagedFile) { if (stagedFile) {
eventHub.$emit(`editor.update.model.new.content.staged-${stagedFile.key}`, stagedFile.content); eventHub.$emit(`editor.update.model.new.content.staged-${stagedFile.key}`, stagedFile.content);
} }
if (openFile && openFile.active) { const file = getters.getStagedFile(path);
const file = state.stagedFiles.find(f => f.path === path);
if (openFile && openFile.active && file) {
dispatch('openPendingTab', { dispatch('openPendingTab', {
file, file,
keyPrefix: stageKeys.staged, keyPrefix: stageKeys.staged,
...@@ -235,14 +235,14 @@ export const stageChange = ({ commit, state, dispatch }, path) => { ...@@ -235,14 +235,14 @@ export const stageChange = ({ commit, state, dispatch }, path) => {
} }
}; };
export const unstageChange = ({ commit, dispatch, state }, path) => { export const unstageChange = ({ commit, dispatch, getters }, path) => {
const openFile = state.openFiles.find(f => f.path === path); const openFile = getters.getOpenFile(path);
commit(types.UNSTAGE_CHANGE, path); commit(types.UNSTAGE_CHANGE, { path, diffInfo: getters.getDiffInfo(path) });
if (openFile && openFile.active) { const file = getters.getChangedFile(path);
const file = state.changedFiles.find(f => f.path === path);
if (openFile && openFile.active && file) {
dispatch('openPendingTab', { dispatch('openPendingTab', {
file, file,
keyPrefix: stageKeys.unstaged, keyPrefix: stageKeys.unstaged,
......
...@@ -64,6 +64,7 @@ export const allBlobs = state => ...@@ -64,6 +64,7 @@ export const allBlobs = state =>
export const getChangedFile = state => path => state.changedFiles.find(f => f.path === path); export const getChangedFile = state => path => state.changedFiles.find(f => f.path === path);
export const getStagedFile = state => path => state.stagedFiles.find(f => f.path === path); export const getStagedFile = state => path => state.stagedFiles.find(f => f.path === path);
export const getOpenFile = state => path => state.openFiles.find(f => f.path === path);
export const lastOpenedFile = state => export const lastOpenedFile = state =>
[...state.changedFiles, ...state.stagedFiles].sort((a, b) => b.lastOpenedAt - a.lastOpenedAt)[0]; [...state.changedFiles, ...state.stagedFiles].sort((a, b) => b.lastOpenedAt - a.lastOpenedAt)[0];
......
...@@ -164,31 +164,32 @@ export default { ...@@ -164,31 +164,32 @@ export default {
changedFiles: state.changedFiles.filter(f => f.path !== path), changedFiles: state.changedFiles.filter(f => f.path !== path),
}); });
}, },
[types.STAGE_CHANGE](state, path) { [types.STAGE_CHANGE](state, { path, diffInfo }) {
const stagedFile = state.stagedFiles.find(f => f.path === path); const stagedFile = state.stagedFiles.find(f => f.path === path);
Object.assign(state, { Object.assign(state, {
changedFiles: state.changedFiles.filter(f => f.path !== path), changedFiles: state.changedFiles.filter(f => f.path !== path),
entries: Object.assign(state.entries, { entries: Object.assign(state.entries, {
[path]: Object.assign(state.entries[path], { [path]: Object.assign(state.entries[path], {
staged: true, staged: diffInfo.exists,
changed: diffInfo.changed,
tempFile: diffInfo.tempFile,
deleted: diffInfo.deleted,
}), }),
}), }),
}); });
if (stagedFile) { if (stagedFile) {
Object.assign(stagedFile, { Object.assign(stagedFile, { ...state.entries[path] });
...state.entries[path],
});
} else { } else {
Object.assign(state, { state.stagedFiles = [...state.stagedFiles, { ...state.entries[path] }];
stagedFiles: state.stagedFiles.concat({ }
...state.entries[path],
}), if (!diffInfo.exists) {
}); state.stagedFiles = state.stagedFiles.filter(f => f.path !== path);
} }
}, },
[types.UNSTAGE_CHANGE](state, path) { [types.UNSTAGE_CHANGE](state, { path, diffInfo }) {
const changedFile = state.changedFiles.find(f => f.path === path); const changedFile = state.changedFiles.find(f => f.path === path);
const stagedFile = state.stagedFiles.find(f => f.path === path); const stagedFile = state.stagedFiles.find(f => f.path === path);
...@@ -201,9 +202,11 @@ export default { ...@@ -201,9 +202,11 @@ export default {
changed: true, changed: true,
}); });
Object.assign(state, { state.changedFiles = state.changedFiles.concat(state.entries[path]);
changedFiles: state.changedFiles.concat(state.entries[path]), }
});
if (!diffInfo.exists) {
state.changedFiles = state.changedFiles.filter(f => f.path !== path);
} }
Object.assign(state, { Object.assign(state, {
...@@ -211,6 +214,9 @@ export default { ...@@ -211,6 +214,9 @@ export default {
entries: Object.assign(state.entries, { entries: Object.assign(state.entries, {
[path]: Object.assign(state.entries[path], { [path]: Object.assign(state.entries[path], {
staged: false, staged: false,
changed: diffInfo.changed,
tempFile: diffInfo.tempFile,
deleted: diffInfo.deleted,
}), }),
}), }),
}); });
......
---
title: "!21542 Part 3: Handle edge cases in stage and unstage mutations"
merge_request: 21676
author:
type: fixed
---
title: Turns on backend MR reports for DAST by default
merge_request: 22001
author:
type: changed
...@@ -57,7 +57,7 @@ Parameters: ...@@ -57,7 +57,7 @@ Parameters:
Creates a two-way relation between two issues. User must be allowed to update both issues in order to succeed. Creates a two-way relation between two issues. User must be allowed to update both issues in order to succeed.
``` ```
POST /projects/:id/issues/:issue_iid/links/:target_project_id/:target_issue_iid POST /projects/:id/issues/:issue_iid/links
``` ```
| Attribute | Type | Required | Description | | Attribute | Type | Required | Description |
...@@ -67,6 +67,12 @@ POST /projects/:id/issues/:issue_iid/links/:target_project_id/:target_issue_iid ...@@ -67,6 +67,12 @@ POST /projects/:id/issues/:issue_iid/links/:target_project_id/:target_issue_iid
| `target_project_id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) of a target project | | `target_project_id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) of a target project |
| `target_issue_iid` | integer/string | yes | The internal ID of a target project's issue | | `target_issue_iid` | integer/string | yes | The internal ID of a target project's issue |
```bash
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/4/issues/1/links?target_project_id=5&target_issue_iid=1"
```
Example response:
```json ```json
{ {
"source_issue" : { "source_issue" : {
......
...@@ -22,3 +22,9 @@ guide you through the process. Or check out how other members of the commmunity ...@@ -22,3 +22,9 @@ guide you through the process. Or check out how other members of the commmunity
are adding support for [PHP](https://gitlab.com/gitlab-org/gitlab/merge_requests/17417) or [Terraform](https://gitlab.com/gitlab-org/gitlab/merge_requests/18834). are adding support for [PHP](https://gitlab.com/gitlab-org/gitlab/merge_requests/17417) or [Terraform](https://gitlab.com/gitlab-org/gitlab/merge_requests/18834).
NOTE: **Note** We are especially interested in adding support for [PyPi](https://gitlab.com/gitlab-org/gitlab/issues/10483), [RubyGems](https://gitlab.com/gitlab-org/gitlab/issues/803), [Debian](https://gitlab.com/gitlab-org/gitlab/issues/5835), and [RPM](https://gitlab.com/gitlab-org/gitlab/issues/5932). NOTE: **Note** We are especially interested in adding support for [PyPi](https://gitlab.com/gitlab-org/gitlab/issues/10483), [RubyGems](https://gitlab.com/gitlab-org/gitlab/issues/803), [Debian](https://gitlab.com/gitlab-org/gitlab/issues/5835), and [RPM](https://gitlab.com/gitlab-org/gitlab/issues/5932).
## Package workflows
Learning how to use the GitLab Package Registry will help you build your own custom package workflow.
[Use a project as a package registry](./workflows/project_registry.md) to publish all of your packages to one project.
# Project as a package registry
Using the features of the package registry, it is possible to use one project to store all of your packages.
This guide mirrors the creation of [this package registry](https://gitlab.com/sabrams/my-package-registry).
For the video version, see [Single Project Package Registry Demo](https://youtu.be/ui2nNBwN35c).
## How does this work?
You might be wondering "how is it possible to upload two packages from different codebases to the same project on GitLab?".
It is easy to forget that a package on GitLab belongs to a project, but a project does not have to be a code repository.
The code used to build your packages can be stored anywhere - maybe it is another project on GitLab, or maybe a completely
different system altogether. All that matters is that when you configure your remote repositories for those packages, you
point them at the same project on GitLab.
## Why would I do this?
There are a few reasons you might want to publish all your packages to one project on GitLab:
1. You want to publish your packages on GitLab, but to a project that is different from where your code is stored.
1. You would like to group packages together in ways that make sense for your usage (all NPM packages in one project,
all packages being used by a specific department in one project, all private packages in one project, etc.)
1. You want to use one remote for all of your packages when installing them into other projects.
1. You would like to migrate your packages to a single place on GitLab from a third-party package registry and do not
want to worry about setting up separate projects for each package.
1. You want to have your CI pipelines build all of your packages to one project so the individual responsible for
validating packages can manage them all in one place.
## Example walkthrough
There is no functionality specific to this feature. All we are doing is taking advantage of functionality available in each
of the package management systems to publish packages of different types to the same place.
Let's take a look at how you might create a public place to hold all of your public packages.
### Create a project
First, create a new project on GitLab. It does not have to have any code or content. Make note of the project ID
displayed on the project overview page, as you will need this later.
### Create an access token
All of the package repositories available on the GitLab package registry are accessible using [GitLab personal access
tokens](../../profile/personal_access_tokens.md).
While using CI, you can alternatively use CI job tokens (`CI_JOB_TOKEN`) to authenticate.
### Configure your local project for the GitLab registry and upload
There are many ways to use this feature. You can upload all types of packages to the same project,
split things up based on package type, or package visibility level.
The purpose of this tutorial is to demonstrate the root idea that one project can hold many unrelated
packages, and to allow you to discover the best way to use this functionality yourself.
#### NPM
If you are using NPM, this involves creating an `.npmrc` file and adding the appropriate URL for uploading packages
to your project using your project ID, then adding a section to your `package.json` file with a similar URL.
Follow
the instructions in the [GitLab NPM Registry documentation](../npm_registry/index.md#authenticating-to-the-gitlab-npm-registry). Once
you do this, you will be able to push your NPM package to your project using `npm publish`, as described in the
[uploading packages](../npm_registry/index.md#uploading-packages) section of the docs.
#### Maven
If you are using Maven, this involves updating your `pom.xml` file with distribution sections, including the
appropriate URL for your project, as described in the [GitLab Maven Repository documentation](../maven_repository/index.md#project-level-maven-endpoint).
Then, you need to add a `settings.xml` file and [include your access token](../maven_repository/index.md#authenticating-with-a-personal-access-token).
Now you can [deploy Maven packages](../maven_repository/index.md#uploading-packages) to your project.
#### Conan
For Conan, first you need to add GitLab as a Conan registry remote. Follow the instructions in the [GitLab Conan Repository docs](../conan_repository/index.md#setting-the-conan-remote-to-the-gitlab-package-registry)
to do so. Then, create your package using the plus-separated (`+`) project path as your Conan user. For example,
if your project is located at `https://gitlab.com/foo/bar/my-proj`, then you can [create your Conan package](../conan_repository/index.md)
using `conan create . foo+bar+my-proj/channel`, where `channel` is your package channel (`stable`, `beta`, etc.). Once your package
is created, you are ready to [upload your package](../conan_repository/index.md#uploading-a-package) depending on your final package recipe. For example:
```sh
CONAN_LOGIN_USERNAME=<gitlab-username> CONAN_PASSWORD=<personal_access_token> conan upload MyPackage/1.0.0@foo+bar+my-proj/channel --all --remote=gitlab
```
export * from '@gitlab/ui';
/**
* The @gitlab/ui tooltip directive requires awkward and distracting set up in tests
* for components that use it (e.g., `attachToDocument: true` and `sync: true` passed
* to the `mount` helper from `vue-test-utils`).
*
* This mock decouples those tests from the implementation, removing the need to set
* them up specially just for these tooltips.
*/
export const GlTooltipDirective = {
bind() {},
};
export const GlTooltip = {
render(h) {
return h('div', this.$attrs, this.$slots.default);
},
};
...@@ -267,17 +267,13 @@ describe('Issue card component', () => { ...@@ -267,17 +267,13 @@ describe('Issue card component', () => {
}); });
it('renders label', () => { it('renders label', () => {
const nodes = wrapper const nodes = wrapper.findAll('.badge').wrappers.map(label => label.attributes('title'));
.findAll('.badge')
.wrappers.map(label => label.attributes('data-original-title'));
expect(nodes.includes(label1.description)).toBe(true); expect(nodes.includes(label1.description)).toBe(true);
}); });
it('sets label description as title', () => { it('sets label description as title', () => {
expect(wrapper.find('.badge').attributes('data-original-title')).toContain( expect(wrapper.find('.badge').attributes('title')).toContain(label1.description);
label1.description,
);
}); });
it('sets background color of button', () => { it('sets background color of button', () => {
......
...@@ -49,7 +49,7 @@ describe('CompareVersions', () => { ...@@ -49,7 +49,7 @@ describe('CompareVersions', () => {
const treeListBtn = wrapper.find('.js-toggle-tree-list'); const treeListBtn = wrapper.find('.js-toggle-tree-list');
expect(treeListBtn.exists()).toBe(true); expect(treeListBtn.exists()).toBe(true);
expect(treeListBtn.attributes('data-original-title')).toBe('Hide file browser'); expect(treeListBtn.attributes('title')).toBe('Hide file browser');
expect(treeListBtn.findAll(Icon).length).not.toBe(0); expect(treeListBtn.findAll(Icon).length).not.toBe(0);
expect(treeListBtn.find(Icon).props('name')).toBe('collapse-left'); expect(treeListBtn.find(Icon).props('name')).toBe('collapse-left');
}); });
......
...@@ -329,7 +329,7 @@ describe('DiffFileHeader component', () => { ...@@ -329,7 +329,7 @@ describe('DiffFileHeader component', () => {
addMergeRequestButtons: true, addMergeRequestButtons: true,
}); });
expect(findViewFileButton().attributes('href')).toBe(viewPath); expect(findViewFileButton().attributes('href')).toBe(viewPath);
expect(findViewFileButton().attributes('data-original-title')).toEqual( expect(findViewFileButton().attributes('title')).toEqual(
`View file @ ${diffFile.content_sha.substr(0, 8)}`, `View file @ ${diffFile.content_sha.substr(0, 8)}`,
); );
}); });
......
...@@ -33,7 +33,7 @@ describe('Monitoring Component', () => { ...@@ -33,7 +33,7 @@ describe('Monitoring Component', () => {
it('should render a link to environment monitoring page', () => { it('should render a link to environment monitoring page', () => {
expect(wrapper.attributes('href')).toEqual(monitoringUrl); expect(wrapper.attributes('href')).toEqual(monitoringUrl);
expect(findIconsByName('chart').length).toBe(1); expect(findIconsByName('chart').length).toBe(1);
expect(wrapper.attributes('data-original-title')).toBe('Monitoring'); expect(wrapper.attributes('title')).toBe('Monitoring');
expect(wrapper.attributes('aria-label')).toBe('Monitoring'); expect(wrapper.attributes('aria-label')).toBe('Monitoring');
}); });
}); });
...@@ -29,7 +29,7 @@ describe('Stop Component', () => { ...@@ -29,7 +29,7 @@ describe('Stop Component', () => {
it('should render a button to stop the environment', () => { it('should render a button to stop the environment', () => {
expect(findButton().exists()).toBe(true); expect(findButton().exists()).toBe(true);
expect(wrapper.attributes('data-original-title')).toEqual('Stop environment'); expect(wrapper.attributes('title')).toEqual('Stop environment');
}); });
it('emits requestStopEnvironment in the event hub when button is clicked', () => { it('emits requestStopEnvironment in the event hub when button is clicked', () => {
......
...@@ -25,7 +25,7 @@ describe('Stop Component', () => { ...@@ -25,7 +25,7 @@ describe('Stop Component', () => {
it('should render a link to open a web terminal with the provided path', () => { it('should render a link to open a web terminal with the provided path', () => {
expect(wrapper.is('a')).toBe(true); expect(wrapper.is('a')).toBe(true);
expect(wrapper.attributes('data-original-title')).toBe('Terminal'); expect(wrapper.attributes('title')).toBe('Terminal');
expect(wrapper.attributes('aria-label')).toBe('Terminal'); expect(wrapper.attributes('aria-label')).toBe('Terminal');
expect(wrapper.attributes('href')).toBe(terminalPath); expect(wrapper.attributes('href')).toBe(terminalPath);
}); });
......
import Vue from 'vue'; import Vue from 'vue';
import MockAdapter from 'axios-mock-adapter'; import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import store from '~/ide/stores'; import { createStore } from '~/ide/stores';
import * as actions from '~/ide/stores/actions/file'; import * as actions from '~/ide/stores/actions/file';
import * as types from '~/ide/stores/mutation_types'; import * as types from '~/ide/stores/mutation_types';
import service from '~/ide/services'; import service from '~/ide/services';
import router from '~/ide/ide_router'; import router from '~/ide/ide_router';
import eventHub from '~/ide/eventhub'; import eventHub from '~/ide/eventhub';
import { file, resetStore } from '../../helpers'; import { file } from '../../helpers';
import testAction from '../../../helpers/vuex_action_helper';
const RELATIVE_URL_ROOT = '/gitlab'; const RELATIVE_URL_ROOT = '/gitlab';
describe('IDE store file actions', () => { describe('IDE store file actions', () => {
let mock; let mock;
let originalGon; let originalGon;
let store;
beforeEach(() => { beforeEach(() => {
mock = new MockAdapter(axios); mock = new MockAdapter(axios);
...@@ -24,12 +24,15 @@ describe('IDE store file actions', () => { ...@@ -24,12 +24,15 @@ describe('IDE store file actions', () => {
relative_url_root: RELATIVE_URL_ROOT, relative_url_root: RELATIVE_URL_ROOT,
}; };
store = createStore();
jest.spyOn(store, 'commit');
jest.spyOn(store, 'dispatch');
jest.spyOn(router, 'push').mockImplementation(() => {}); jest.spyOn(router, 'push').mockImplementation(() => {});
}); });
afterEach(() => { afterEach(() => {
mock.restore(); mock.restore();
resetStore(store);
window.gon = originalGon; window.gon = originalGon;
}); });
...@@ -663,30 +666,33 @@ describe('IDE store file actions', () => { ...@@ -663,30 +666,33 @@ describe('IDE store file actions', () => {
}); });
describe('stageChange', () => { describe('stageChange', () => {
it('calls STAGE_CHANGE with file path', done => { it('calls STAGE_CHANGE with file path', () => {
testAction( const f = { ...file('path'), content: 'old' };
actions.stageChange,
'path', store.state.entries[f.path] = f;
store.state,
[ actions.stageChange(store, 'path');
{ type: types.STAGE_CHANGE, payload: 'path' },
{ type: types.SET_LAST_COMMIT_MSG, payload: '' }, expect(store.commit).toHaveBeenCalledWith(
], types.STAGE_CHANGE,
[], expect.objectContaining({ path: 'path' }),
done,
); );
expect(store.commit).toHaveBeenCalledWith(types.SET_LAST_COMMIT_MSG, '');
}); });
}); });
describe('unstageChange', () => { describe('unstageChange', () => {
it('calls UNSTAGE_CHANGE with file path', done => { it('calls UNSTAGE_CHANGE with file path', () => {
testAction( const f = { ...file('path'), content: 'old' };
actions.unstageChange,
'path', store.state.entries[f.path] = f;
store.state, store.state.stagedFiles.push({ f, content: 'new' });
[{ type: types.UNSTAGE_CHANGE, payload: 'path' }],
[], actions.unstageChange(store, 'path');
done,
expect(store.commit).toHaveBeenCalledWith(
types.UNSTAGE_CHANGE,
expect.objectContaining({ path: 'path' }),
); );
}); });
}); });
......
import mutations from '~/ide/stores/mutations/file'; import mutations from '~/ide/stores/mutations/file';
import state from '~/ide/stores/state'; import { createStore } from '~/ide/stores';
import { FILE_VIEW_MODE_PREVIEW } from '~/ide/constants'; import { FILE_VIEW_MODE_PREVIEW } from '~/ide/constants';
import { file } from '../../helpers'; import { file } from '../../helpers';
describe('IDE store file mutations', () => { describe('IDE store file mutations', () => {
let localState; let localState;
let localStore;
let localFile; let localFile;
beforeEach(() => { beforeEach(() => {
localState = state(); localStore = createStore();
localState = localStore.state;
localFile = { ...file(), type: 'blob' }; localFile = { ...file(), type: 'blob' };
localState.entries[localFile.path] = localFile; localState.entries[localFile.path] = localFile;
...@@ -333,44 +335,154 @@ describe('IDE store file mutations', () => { ...@@ -333,44 +335,154 @@ describe('IDE store file mutations', () => {
}); });
}); });
describe('STAGE_CHANGE', () => { describe.each`
beforeEach(() => { mutationName | mutation | addedTo | removedFrom | staged | changedFilesCount | stagedFilesCount
mutations.STAGE_CHANGE(localState, localFile.path); ${'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('adds file into stagedFiles array', () => { it('removes all changes of a file if staged and unstaged change contents are equal', () => {
expect(localState.stagedFiles.length).toBe(1); unstagedFile.content = 'original content';
expect(localState.stagedFiles[0]).toEqual(localFile);
});
it('updates stagedFile if it is already staged', () => { mutation(localState, {
localFile.raw = 'testing 123'; path: unstagedFile.path,
diffInfo: localStore.getters.getDiffInfo(unstagedFile.path),
});
mutations.STAGE_CHANGE(localState, localFile.path); expect(localState.entries.file).toEqual(
expect.objectContaining({
content: 'original content',
staged: false,
changed: false,
}),
);
expect(localState.stagedFiles.length).toBe(1); expect(localState.stagedFiles.length).toBe(0);
expect(localState.stagedFiles[0].raw).toEqual('testing 123'); expect(localState.changedFiles.length).toBe(0);
}); });
});
describe('UNSTAGE_CHANGE', () => { it('removes all changes of a file if a file is deleted and a new file with same content is added', () => {
let f; stagedFile.deleted = true;
unstagedFile.tempFile = true;
unstagedFile.content = 'original content';
beforeEach(() => { mutation(localState, {
f = { ...file(), type: 'blob', staged: true }; path: unstagedFile.path,
diffInfo: localStore.getters.getDiffInfo(unstagedFile.path),
});
localState.stagedFiles.push(f); expect(localState.stagedFiles.length).toBe(0);
localState.changedFiles.push(f); expect(localState.changedFiles.length).toBe(0);
localState.entries[f.path] = f;
});
it('removes from stagedFiles array', () => { expect(localState.entries.file).toEqual(
mutations.UNSTAGE_CHANGE(localState, f.path); expect.objectContaining({
content: 'original content',
deleted: false,
tempFile: false,
}),
);
});
expect(localState.stagedFiles.length).toBe(0); it('merges deleted and added file into a changed file if the contents differ', () => {
expect(localState.changedFiles.length).toBe(1); 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', () => { describe('TOGGLE_FILE_CHANGED', () => {
it('updates file changed status', () => { it('updates file changed status', () => {
......
...@@ -135,7 +135,7 @@ describe('Issuable suggestions suggestion component', () => { ...@@ -135,7 +135,7 @@ describe('Issuable suggestions suggestion component', () => {
const icon = vm.find(Icon); const icon = vm.find(Icon);
expect(icon.props('name')).toBe('eye-slash'); expect(icon.props('name')).toBe('eye-slash');
expect(icon.attributes('data-original-title')).toBe('Confidential'); expect(icon.attributes('title')).toBe('Confidential');
}); });
}); });
}); });
...@@ -70,7 +70,7 @@ describe('Issuable component', () => { ...@@ -70,7 +70,7 @@ describe('Issuable component', () => {
const findTaskStatus = () => wrapper.find('.task-status'); const findTaskStatus = () => wrapper.find('.task-status');
const findOpenedAgoContainer = () => wrapper.find({ ref: 'openedAgoByContainer' }); const findOpenedAgoContainer = () => wrapper.find({ ref: 'openedAgoByContainer' });
const findMilestone = () => wrapper.find('.js-milestone'); const findMilestone = () => wrapper.find('.js-milestone');
const findMilestoneTooltip = () => findMilestone().attributes('data-original-title'); const findMilestoneTooltip = () => findMilestone().attributes('title');
const findDueDate = () => wrapper.find('.js-due-date'); const findDueDate = () => wrapper.find('.js-due-date');
const findLabelContainer = () => wrapper.find('.js-labels'); const findLabelContainer = () => wrapper.find('.js-labels');
const findLabelLinks = () => findLabelContainer().findAll(GlLink); const findLabelLinks = () => findLabelContainer().findAll(GlLink);
...@@ -240,7 +240,7 @@ describe('Issuable component', () => { ...@@ -240,7 +240,7 @@ describe('Issuable component', () => {
const labels = findLabelLinks().wrappers.map(label => ({ const labels = findLabelLinks().wrappers.map(label => ({
href: label.attributes('href'), href: label.attributes('href'),
text: label.text(), text: label.text(),
tooltip: label.find('span').attributes('data-original-title'), tooltip: label.find('span').attributes('title'),
})); }));
const expected = testLabels.map(label => ({ const expected = testLabels.map(label => ({
......
...@@ -7,8 +7,7 @@ exports[`JumpToNextDiscussionButton matches the snapshot 1`] = ` ...@@ -7,8 +7,7 @@ exports[`JumpToNextDiscussionButton matches the snapshot 1`] = `
> >
<button <button
class="btn btn-default discussion-next-btn" class="btn btn-default discussion-next-btn"
data-original-title="Jump to next unresolved discussion" title="Jump to next unresolved discussion"
title=""
> >
<icon-stub <icon-stub
name="comment-next" name="comment-next"
......
...@@ -30,7 +30,7 @@ describe('pipeline graph action component', () => { ...@@ -30,7 +30,7 @@ describe('pipeline graph action component', () => {
}); });
it('should render the provided title as a bootstrap tooltip', () => { it('should render the provided title as a bootstrap tooltip', () => {
expect(wrapper.attributes('data-original-title')).toBe('bar'); expect(wrapper.attributes('title')).toBe('bar');
}); });
it('should update bootstrap tooltip when title changes', done => { it('should update bootstrap tooltip when title changes', done => {
...@@ -39,7 +39,7 @@ describe('pipeline graph action component', () => { ...@@ -39,7 +39,7 @@ describe('pipeline graph action component', () => {
wrapper.vm wrapper.vm
.$nextTick() .$nextTick()
.then(() => { .then(() => {
expect(wrapper.attributes('data-original-title')).toBe('changed'); expect(wrapper.attributes('title')).toBe('changed');
}) })
.then(done) .then(done)
.catch(done.fail); .catch(done.fail);
......
...@@ -43,9 +43,7 @@ describe('pipeline graph job item', () => { ...@@ -43,9 +43,7 @@ describe('pipeline graph job item', () => {
expect(link.attributes('href')).toBe(mockJob.status.details_path); expect(link.attributes('href')).toBe(mockJob.status.details_path);
expect(link.attributes('data-original-title')).toEqual( expect(link.attributes('title')).toEqual(`${mockJob.name} - ${mockJob.status.label}`);
`${mockJob.name} - ${mockJob.status.label}`,
);
expect(wrapper.find('.js-status-icon-success')).toBeDefined(); expect(wrapper.find('.js-status-icon-success')).toBeDefined();
...@@ -110,9 +108,7 @@ describe('pipeline graph job item', () => { ...@@ -110,9 +108,7 @@ describe('pipeline graph job item', () => {
}, },
}); });
expect(wrapper.find('.js-job-component-tooltip').attributes('data-original-title')).toBe( expect(wrapper.find('.js-job-component-tooltip').attributes('title')).toBe('test');
'test',
);
}); });
it('should not render status label when it is provided', () => { it('should not render status label when it is provided', () => {
...@@ -128,7 +124,7 @@ describe('pipeline graph job item', () => { ...@@ -128,7 +124,7 @@ describe('pipeline graph job item', () => {
}, },
}); });
expect(wrapper.find('.js-job-component-tooltip').attributes('data-original-title')).toEqual( expect(wrapper.find('.js-job-component-tooltip').attributes('title')).toEqual(
'test - success', 'test - success',
); );
}); });
...@@ -140,7 +136,7 @@ describe('pipeline graph job item', () => { ...@@ -140,7 +136,7 @@ describe('pipeline graph job item', () => {
job: delayedJobFixture, job: delayedJobFixture,
}); });
expect(wrapper.find('.js-pipeline-graph-job-link').attributes('data-original-title')).toEqual( expect(wrapper.find('.js-pipeline-graph-job-link').attributes('title')).toEqual(
`delayed job - delayed manual action (${wrapper.vm.remainingTime})`, `delayed job - delayed manual action (${wrapper.vm.remainingTime})`,
); );
}); });
......
...@@ -65,7 +65,7 @@ describe('Linked pipeline', () => { ...@@ -65,7 +65,7 @@ describe('Linked pipeline', () => {
it('should render the tooltip text as the title attribute', () => { it('should render the tooltip text as the title attribute', () => {
const tooltipRef = wrapper.find('.js-linked-pipeline-content'); const tooltipRef = wrapper.find('.js-linked-pipeline-content');
const titleAttr = tooltipRef.attributes('data-original-title'); const titleAttr = tooltipRef.attributes('title');
expect(titleAttr).toContain(mockPipeline.project.name); expect(titleAttr).toContain(mockPipeline.project.name);
expect(titleAttr).toContain(mockPipeline.details.status.label); expect(titleAttr).toContain(mockPipeline.details.status.label);
......
...@@ -105,8 +105,6 @@ describe('Pipeline Url Component', () => { ...@@ -105,8 +105,6 @@ describe('Pipeline Url Component', () => {
}); });
expect(wrapper.find('.js-pipeline-url-failure').text()).toContain('error'); expect(wrapper.find('.js-pipeline-url-failure').text()).toContain('error');
expect(wrapper.find('.js-pipeline-url-failure').attributes('data-original-title')).toContain( expect(wrapper.find('.js-pipeline-url-failure').attributes('title')).toContain('some reason');
'some reason',
);
}); });
}); });
...@@ -86,8 +86,7 @@ exports[`Registry Project Empty state to match the default snapshot 1`] = ` ...@@ -86,8 +86,7 @@ exports[`Registry Project Empty state to match the default snapshot 1`] = `
<button <button
class="btn input-group-text btn-secondary btn-default" class="btn input-group-text btn-secondary btn-default"
data-clipboard-text="docker login host" data-clipboard-text="docker login host"
data-original-title="Copy login command" title="Copy login command"
title=""
type="button" type="button"
> >
<svg <svg
...@@ -125,8 +124,7 @@ exports[`Registry Project Empty state to match the default snapshot 1`] = ` ...@@ -125,8 +124,7 @@ exports[`Registry Project Empty state to match the default snapshot 1`] = `
<button <button
class="btn input-group-text btn-secondary btn-default" class="btn input-group-text btn-secondary btn-default"
data-clipboard-text="docker build -t url ." data-clipboard-text="docker build -t url ."
data-original-title="Copy build command" title="Copy build command"
title=""
type="button" type="button"
> >
<svg <svg
...@@ -156,8 +154,7 @@ exports[`Registry Project Empty state to match the default snapshot 1`] = ` ...@@ -156,8 +154,7 @@ exports[`Registry Project Empty state to match the default snapshot 1`] = `
<button <button
class="btn input-group-text btn-secondary btn-default" class="btn input-group-text btn-secondary btn-default"
data-clipboard-text="docker push url" data-clipboard-text="docker push url"
data-original-title="Copy push command" title="Copy push command"
title=""
type="button" type="button"
> >
<svg <svg
......
...@@ -39,7 +39,7 @@ describe('Evidence Block', () => { ...@@ -39,7 +39,7 @@ describe('Evidence Block', () => {
}); });
it('renders the correct hover text for the download', () => { it('renders the correct hover text for the download', () => {
expect(wrapper.find(GlLink).attributes('data-original-title')).toBe('Download evidence JSON'); expect(wrapper.find(GlLink).attributes('title')).toBe('Download evidence JSON');
}); });
it('renders the correct file link for download', () => { it('renders the correct file link for download', () => {
...@@ -63,9 +63,7 @@ describe('Evidence Block', () => { ...@@ -63,9 +63,7 @@ describe('Evidence Block', () => {
}); });
it('renders the correct hover text', () => { it('renders the correct hover text', () => {
expect(wrapper.find(ClipboardButton).attributes('data-original-title')).toBe( expect(wrapper.find(ClipboardButton).attributes('title')).toBe('Copy commit SHA');
'Copy commit SHA',
);
}); });
it('copies the sha', () => { it('copies the sha', () => {
......
...@@ -61,7 +61,7 @@ describe('Release block milestone info', () => { ...@@ -61,7 +61,7 @@ describe('Release block milestone info', () => {
expect(milestoneLink.text()).toBe(m.title); expect(milestoneLink.text()).toBe(m.title);
expect(milestoneLink.attributes('href')).toBe(m.web_url); expect(milestoneLink.attributes('href')).toBe(m.web_url);
expect(milestoneLink.attributes('data-original-title')).toBe(m.description); expect(milestoneLink.attributes('title')).toBe(m.description);
}); });
}); });
......
...@@ -271,7 +271,7 @@ describe('Release block', () => { ...@@ -271,7 +271,7 @@ describe('Release block', () => {
expect(milestoneLink.attributes('href')).toBe(milestone.web_url); expect(milestoneLink.attributes('href')).toBe(milestone.web_url);
expect(milestoneLink.attributes('data-original-title')).toBe(milestone.description); expect(milestoneLink.attributes('title')).toBe(milestone.description);
}); });
}); });
......
...@@ -67,9 +67,8 @@ exports[`Repository last commit component renders commit widget 1`] = ` ...@@ -67,9 +67,8 @@ exports[`Repository last commit component renders commit widget 1`] = `
> >
<gllink-stub <gllink-stub
class="js-commit-pipeline" class="js-commit-pipeline"
data-original-title="Commit: failed"
href="https://test.com/pipeline" href="https://test.com/pipeline"
title="" title="Commit: failed"
> >
<ciicon-stub <ciicon-stub
aria-label="Commit: failed" aria-label="Commit: failed"
...@@ -174,9 +173,8 @@ exports[`Repository last commit component renders the signature HTML as returned ...@@ -174,9 +173,8 @@ exports[`Repository last commit component renders the signature HTML as returned
> >
<gllink-stub <gllink-stub
class="js-commit-pipeline" class="js-commit-pipeline"
data-original-title="Commit: failed"
href="https://test.com/pipeline" href="https://test.com/pipeline"
title="" title="Commit: failed"
> >
<ciicon-stub <ciicon-stub
aria-label="Commit: failed" aria-label="Commit: failed"
......
...@@ -178,7 +178,7 @@ describe('Assignee component', () => { ...@@ -178,7 +178,7 @@ describe('Assignee component', () => {
const userItems = wrapper.findAll('.user-list .user-item a'); const userItems = wrapper.findAll('.user-list .user-item a');
expect(userItems.length).toBe(3); expect(userItems.length).toBe(3);
expect(userItems.at(0).attributes('data-original-title')).toBe(users[2].name); expect(userItems.at(0).attributes('title')).toBe(users[2].name);
}); });
it('passes the sorted assignees to the collapsed-assignee-list', () => { it('passes the sorted assignees to the collapsed-assignee-list', () => {
......
...@@ -33,7 +33,7 @@ describe('AssigneeAvatarLink component', () => { ...@@ -33,7 +33,7 @@ describe('AssigneeAvatarLink component', () => {
wrapper.destroy(); wrapper.destroy();
}); });
const findTooltipText = () => wrapper.attributes('data-original-title'); const findTooltipText = () => wrapper.attributes('title');
it('has the root url present in the assigneeUrl method', () => { it('has the root url present in the assigneeUrl method', () => {
createComponent(); createComponent();
......
...@@ -25,7 +25,7 @@ describe('CollapsedAssigneeList component', () => { ...@@ -25,7 +25,7 @@ describe('CollapsedAssigneeList component', () => {
const findNoUsersIcon = () => wrapper.find('i[aria-label=None]'); const findNoUsersIcon = () => wrapper.find('i[aria-label=None]');
const findAvatarCounter = () => wrapper.find('.avatar-counter'); const findAvatarCounter = () => wrapper.find('.avatar-counter');
const findAssignees = () => wrapper.findAll(CollapsedAssignee); const findAssignees = () => wrapper.findAll(CollapsedAssignee);
const getTooltipTitle = () => wrapper.attributes('data-original-title'); const getTooltipTitle = () => wrapper.attributes('title');
afterEach(() => { afterEach(() => {
wrapper.destroy(); wrapper.destroy();
......
...@@ -30,7 +30,7 @@ describe('Changed file icon', () => { ...@@ -30,7 +30,7 @@ describe('Changed file icon', () => {
const findIcon = () => wrapper.find(Icon); const findIcon = () => wrapper.find(Icon);
const findIconName = () => findIcon().props('name'); const findIconName = () => findIcon().props('name');
const findIconClasses = () => findIcon().classes(); const findIconClasses = () => findIcon().classes();
const findTooltipText = () => wrapper.attributes('data-original-title'); const findTooltipText = () => wrapper.attributes('title');
it('with isCentered true, adds center class', () => { it('with isCentered true, adds center class', () => {
factory({ factory({
...@@ -89,7 +89,7 @@ describe('Changed file icon', () => { ...@@ -89,7 +89,7 @@ describe('Changed file icon', () => {
}); });
it('does not have tooltip text', () => { it('does not have tooltip text', () => {
expect(findTooltipText()).toBe(''); expect(findTooltipText()).toBeFalsy();
}); });
}); });
......
...@@ -35,7 +35,7 @@ describe('clipboard button', () => { ...@@ -35,7 +35,7 @@ describe('clipboard button', () => {
}); });
it('should have a tooltip with default values', () => { it('should have a tooltip with default values', () => {
expect(wrapper.attributes('data-original-title')).toBe('Copy this value'); expect(wrapper.attributes('title')).toBe('Copy this value');
}); });
it('should render provided classname', () => { it('should render provided classname', () => {
......
...@@ -160,7 +160,7 @@ describe('Commit component', () => { ...@@ -160,7 +160,7 @@ describe('Commit component', () => {
expect(refEl.attributes('href')).toBe(props.commitRef.ref_url); expect(refEl.attributes('href')).toBe(props.commitRef.ref_url);
expect(refEl.attributes('data-original-title')).toBe(props.commitRef.name); expect(refEl.attributes('title')).toBe(props.commitRef.name);
expect(wrapper.find('icon-stub[name="branch"]').exists()).toBe(true); expect(wrapper.find('icon-stub[name="branch"]').exists()).toBe(true);
}); });
...@@ -193,7 +193,7 @@ describe('Commit component', () => { ...@@ -193,7 +193,7 @@ describe('Commit component', () => {
expect(refEl.attributes('href')).toBe(props.mergeRequestRef.path); expect(refEl.attributes('href')).toBe(props.mergeRequestRef.path);
expect(refEl.attributes('data-original-title')).toBe(props.mergeRequestRef.title); expect(refEl.attributes('title')).toBe(props.mergeRequestRef.title);
expect(wrapper.find('icon-stub[name="git-merge"]').exists()).toBe(true); expect(wrapper.find('icon-stub[name="git-merge"]').exists()).toBe(true);
}); });
......
...@@ -66,7 +66,7 @@ describe('IssueAssigneesComponent', () => { ...@@ -66,7 +66,7 @@ describe('IssueAssigneesComponent', () => {
expect(findOverflowCounter().exists()).toBe(true); expect(findOverflowCounter().exists()).toBe(true);
expect(findOverflowCounter().text()).toEqual(expectedHidden.toString()); expect(findOverflowCounter().text()).toEqual(expectedHidden.toString());
expect(findOverflowCounter().attributes('data-original-title')).toEqual( expect(findOverflowCounter().attributes('title')).toEqual(
`${hiddenCount} more assignees`, `${hiddenCount} more assignees`,
); );
}); });
......
...@@ -25,7 +25,7 @@ describe('Time ago with tooltip component', () => { ...@@ -25,7 +25,7 @@ describe('Time ago with tooltip component', () => {
}); });
const timeago = getTimeago(); const timeago = getTimeago();
expect(vm.attributes('data-original-title')).toEqual(formatDate(timestamp)); expect(vm.attributes('title')).toEqual(formatDate(timestamp));
expect(vm.text()).toEqual(timeago.format(timestamp)); expect(vm.text()).toEqual(timeago.format(timestamp));
}); });
......
...@@ -100,7 +100,7 @@ describe('User Avatar Image Component', () => { ...@@ -100,7 +100,7 @@ describe('User Avatar Image Component', () => {
it('does not render tooltip data attributes for on avatar image', () => { it('does not render tooltip data attributes for on avatar image', () => {
const avatarImg = wrapper.find('img'); const avatarImg = wrapper.find('img');
expect(avatarImg.attributes('data-original-title')).toBeFalsy(); expect(avatarImg.attributes('title')).toBeFalsy();
expect(avatarImg.attributes('data-placement')).not.toBeDefined(); expect(avatarImg.attributes('data-placement')).not.toBeDefined();
expect(avatarImg.attributes('data-container')).not.toBeDefined(); expect(avatarImg.attributes('data-container')).not.toBeDefined();
}); });
......
...@@ -99,9 +99,9 @@ describe('User Avatar Link Component', () => { ...@@ -99,9 +99,9 @@ describe('User Avatar Link Component', () => {
}); });
it('should render text tooltip for <span>', () => { it('should render text tooltip for <span>', () => {
expect( expect(wrapper.find('.js-user-avatar-link-username').attributes('title')).toEqual(
wrapper.find('.js-user-avatar-link-username').attributes('data-original-title'), defaultProps.tooltipText,
).toEqual(defaultProps.tooltipText); );
}); });
it('should render text tooltip placement for <span>', () => { it('should render text tooltip placement for <span>', () => {
......
...@@ -18,19 +18,19 @@ import axios from '~/lib/utils/axios_utils'; ...@@ -18,19 +18,19 @@ import axios from '~/lib/utils/axios_utils';
import { createStore } from '~/ide/stores'; import { createStore } from '~/ide/stores';
import * as types from '~/ide/stores/mutation_types'; import * as types from '~/ide/stores/mutation_types';
import router from '~/ide/ide_router'; import router from '~/ide/ide_router';
import { resetStore, file } from '../helpers'; import { file } from '../helpers';
import testAction from '../../helpers/vuex_action_helper'; import testAction from '../../helpers/vuex_action_helper';
import eventHub from '~/ide/eventhub'; import eventHub from '~/ide/eventhub';
const store = createStore();
describe('Multi-file store actions', () => { describe('Multi-file store actions', () => {
let store;
beforeEach(() => { beforeEach(() => {
spyOn(router, 'push'); store = createStore();
});
afterEach(() => { spyOn(store, 'commit').and.callThrough();
resetStore(store); spyOn(store, 'dispatch').and.callThrough();
spyOn(router, 'push');
}); });
describe('redirectToUrl', () => { describe('redirectToUrl', () => {
...@@ -390,58 +390,82 @@ describe('Multi-file store actions', () => { ...@@ -390,58 +390,82 @@ describe('Multi-file store actions', () => {
}); });
}); });
describe('stageAllChanges', () => { describe('stage/unstageAllChanges', () => {
it('adds all files from changedFiles to stagedFiles', done => { let file1;
const openFile = { ...file(), path: 'test' }; let file2;
store.state.openFiles.push(openFile); beforeEach(() => {
store.state.stagedFiles.push(openFile); file1 = { ...file('test'), content: 'changed test', raw: 'test' };
store.state.changedFiles.push(openFile, file('new')); file2 = { ...file('test2'), content: 'changed test2', raw: 'test2' };
testAction( store.state.openFiles = [file1];
stageAllChanges, store.state.changedFiles = [file1];
null, store.state.stagedFiles = [{ ...file2, content: 'staged test' }];
store.state,
[ store.state.entries = {
{ type: types.SET_LAST_COMMIT_MSG, payload: '' }, [file1.path]: { ...file1 },
{ type: types.STAGE_CHANGE, payload: store.state.changedFiles[0].path }, [file2.path]: { ...file2 },
{ type: types.STAGE_CHANGE, payload: store.state.changedFiles[1].path }, };
],
[
{
type: 'openPendingTab',
payload: { file: openFile, keyPrefix: 'staged' },
},
],
done,
);
}); });
});
describe('unstageAllChanges', () => { describe('stageAllChanges', () => {
it('removes all files from stagedFiles after unstaging', done => { it('adds all files from changedFiles to stagedFiles', () => {
const openFile = { ...file(), path: 'test' }; stageAllChanges(store);
store.state.openFiles.push(openFile); expect(store.commit.calls.allArgs()).toEqual([
store.state.changedFiles.push(openFile); [types.SET_LAST_COMMIT_MSG, ''],
store.state.stagedFiles.push(openFile, file('new')); [types.STAGE_CHANGE, jasmine.objectContaining({ path: file1.path })],
]);
});
testAction( it('opens pending tab if a change exists in that file', () => {
unstageAllChanges, stageAllChanges(store);
null,
store.state, expect(store.dispatch.calls.allArgs()).toEqual([
[ [
{ type: types.UNSTAGE_CHANGE, payload: store.state.stagedFiles[0].path }, 'openPendingTab',
{ type: types.UNSTAGE_CHANGE, payload: store.state.stagedFiles[1].path }, { file: { ...file1, staged: true, changed: true }, keyPrefix: 'staged' },
], ],
[ ]);
{ });
type: 'openPendingTab',
payload: { file: openFile, keyPrefix: 'unstaged' }, it('does not open pending tab if no change exists in that file', () => {
}, store.state.entries[file1.path].content = 'test';
], store.state.stagedFiles = [file1];
done, store.state.changedFiles = [store.state.entries[file1.path]];
);
stageAllChanges(store);
expect(store.dispatch).not.toHaveBeenCalled();
});
});
describe('unstageAllChanges', () => {
it('removes all files from stagedFiles after unstaging', () => {
unstageAllChanges(store);
expect(store.commit.calls.allArgs()).toEqual([
[types.UNSTAGE_CHANGE, jasmine.objectContaining({ path: file2.path })],
]);
});
it('opens pending tab if a change exists in that file', () => {
unstageAllChanges(store);
expect(store.dispatch.calls.allArgs()).toEqual([
['openPendingTab', { file: file1, keyPrefix: 'unstaged' }],
]);
});
it('does not open pending tab if no change exists in that file', () => {
store.state.entries[file1.path].content = 'test';
store.state.stagedFiles = [file1];
store.state.changedFiles = [store.state.entries[file1.path]];
unstageAllChanges(store);
expect(store.dispatch).not.toHaveBeenCalled();
});
}); });
}); });
...@@ -752,10 +776,6 @@ describe('Multi-file store actions', () => { ...@@ -752,10 +776,6 @@ describe('Multi-file store actions', () => {
}); });
}); });
afterEach(() => {
resetStore(store);
});
it('by default renames an entry and adds to changed', done => { it('by default renames an entry and adds to changed', done => {
testAction( testAction(
renameEntry, renameEntry,
...@@ -966,18 +986,19 @@ describe('Multi-file store actions', () => { ...@@ -966,18 +986,19 @@ describe('Multi-file store actions', () => {
describe('error', () => { describe('error', () => {
let dispatch; let dispatch;
const callParams = [ let callParams;
{
commit() {},
state: store.state,
},
{
projectId: 'abc/def',
branchId: 'master-testing',
},
];
beforeEach(() => { beforeEach(() => {
callParams = [
{
commit() {},
state: store.state,
},
{
projectId: 'abc/def',
branchId: 'master-testing',
},
];
dispatch = jasmine.createSpy('dispatchSpy'); dispatch = jasmine.createSpy('dispatchSpy');
document.body.innerHTML += '<div class="flash-container"></div>'; document.body.innerHTML += '<div class="flash-container"></div>';
}); });
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment