Commit d7dc5a8f authored by Filipa Lacerda's avatar Filipa Lacerda

Merge branch 'ce-to-ee-2018-09-20' into 'master'

CE upstream - 2018-09-20 09:24 UTC

See merge request gitlab-org/gitlab-ee!7425
parents 3a4c6c3f 8c88ae09
<script> <script>
import { mapGetters } from 'vuex'; import { mapGetters, mapActions } from 'vuex';
import DiffTableCell from './diff_table_cell.vue'; import DiffTableCell from './diff_table_cell.vue';
import { import {
NEW_LINE_TYPE, NEW_LINE_TYPE,
...@@ -63,7 +63,11 @@ export default { ...@@ -63,7 +63,11 @@ export default {
this.linePositionLeft = LINE_POSITION_LEFT; this.linePositionLeft = LINE_POSITION_LEFT;
this.linePositionRight = LINE_POSITION_RIGHT; this.linePositionRight = LINE_POSITION_RIGHT;
}, },
mounted() {
this.scrollToLineIfNeededInline(this.line);
},
methods: { methods: {
...mapActions('diffs', ['scrollToLineIfNeededInline']),
handleMouseMove(e) { handleMouseMove(e) {
// To show the comment icon on the gutter we need to know if we hover the line. // To show the comment icon on the gutter we need to know if we hover the line.
// Current table structure doesn't allow us to do this with CSS in both of the diff view types // Current table structure doesn't allow us to do this with CSS in both of the diff view types
......
<script> <script>
import { mapActions } from 'vuex';
import $ from 'jquery'; import $ from 'jquery';
import DiffTableCell from './diff_table_cell.vue'; import DiffTableCell from './diff_table_cell.vue';
import { import {
...@@ -64,7 +65,11 @@ export default { ...@@ -64,7 +65,11 @@ export default {
this.oldLineType = OLD_LINE_TYPE; this.oldLineType = OLD_LINE_TYPE;
this.parallelDiffViewType = PARALLEL_DIFF_VIEW_TYPE; this.parallelDiffViewType = PARALLEL_DIFF_VIEW_TYPE;
}, },
mounted() {
this.scrollToLineIfNeededParallel(this.line);
},
methods: { methods: {
...mapActions('diffs', ['scrollToLineIfNeededParallel']),
handleMouseMove(e) { handleMouseMove(e) {
const isHover = e.type === 'mouseover'; const isHover = e.type === 'mouseover';
const hoveringCell = e.target.closest('td'); const hoveringCell = e.target.closest('td');
......
...@@ -2,7 +2,7 @@ import Vue from 'vue'; ...@@ -2,7 +2,7 @@ import Vue from 'vue';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
import { handleLocationHash, historyPushState } from '~/lib/utils/common_utils'; import { handleLocationHash, historyPushState } from '~/lib/utils/common_utils';
import { mergeUrlParams } from '~/lib/utils/url_utility'; import { mergeUrlParams, getLocationHash } from '~/lib/utils/url_utility';
import { getDiffPositionByLineCode } from './utils'; import { getDiffPositionByLineCode } from './utils';
import * as types from './mutation_types'; import * as types from './mutation_types';
import { import {
...@@ -120,6 +120,25 @@ export const loadMoreLines = ({ commit }, options) => { ...@@ -120,6 +120,25 @@ export const loadMoreLines = ({ commit }, options) => {
}); });
}; };
export const scrollToLineIfNeededInline = (_, line) => {
const hash = getLocationHash();
if (hash && line.lineCode === hash) {
handleLocationHash();
}
};
export const scrollToLineIfNeededParallel = (_, line) => {
const hash = getLocationHash();
if (
hash &&
((line.left && line.left.lineCode === hash) || (line.right && line.right.lineCode === hash))
) {
handleLocationHash();
}
};
export const loadCollapsedDiff = ({ commit }, file) => export const loadCollapsedDiff = ({ commit }, file) =>
axios.get(file.loadCollapsedDiffUrl).then(res => { axios.get(file.loadCollapsedDiffUrl).then(res => {
commit(types.ADD_COLLAPSED_DIFFS, { commit(types.ADD_COLLAPSED_DIFFS, {
......
...@@ -87,6 +87,7 @@ export const handleLocationHash = () => { ...@@ -87,6 +87,7 @@ export const handleLocationHash = () => {
const fixedTabs = document.querySelector('.js-tabs-affix'); const fixedTabs = document.querySelector('.js-tabs-affix');
const fixedDiffStats = document.querySelector('.js-diff-files-changed'); const fixedDiffStats = document.querySelector('.js-diff-files-changed');
const fixedNav = document.querySelector('.navbar-gitlab'); const fixedNav = document.querySelector('.navbar-gitlab');
const performanceBar = document.querySelector('#js-peek');
let adjustment = 0; let adjustment = 0;
if (fixedNav) adjustment -= fixedNav.offsetHeight; if (fixedNav) adjustment -= fixedNav.offsetHeight;
...@@ -103,6 +104,10 @@ export const handleLocationHash = () => { ...@@ -103,6 +104,10 @@ export const handleLocationHash = () => {
adjustment -= fixedDiffStats.offsetHeight; adjustment -= fixedDiffStats.offsetHeight;
} }
if (performanceBar) {
adjustment -= performanceBar.offsetHeight;
}
window.scrollBy(0, adjustment); window.scrollBy(0, adjustment);
}; };
...@@ -132,21 +137,20 @@ export const parseUrlPathname = url => { ...@@ -132,21 +137,20 @@ export const parseUrlPathname = url => {
return parsedUrl.pathname.charAt(0) === '/' ? parsedUrl.pathname : `/${parsedUrl.pathname}`; return parsedUrl.pathname.charAt(0) === '/' ? parsedUrl.pathname : `/${parsedUrl.pathname}`;
}; };
const splitPath = (path = '') => path const splitPath = (path = '') => path.replace(/^\?/, '').split('&');
.replace(/^\?/, '')
.split('&');
export const urlParamsToArray = (path = '') => splitPath(path) export const urlParamsToArray = (path = '') =>
.filter(param => param.length > 0) splitPath(path)
.map(param => { .filter(param => param.length > 0)
const split = param.split('='); .map(param => {
return [decodeURI(split[0]), split[1]].join('='); const split = param.split('=');
}); return [decodeURI(split[0]), split[1]].join('=');
});
export const getUrlParamsArray = () => urlParamsToArray(window.location.search); export const getUrlParamsArray = () => urlParamsToArray(window.location.search);
export const urlParamsToObject = (path = '') => splitPath(path) export const urlParamsToObject = (path = '') =>
.reduce((dataParam, filterParam) => { splitPath(path).reduce((dataParam, filterParam) => {
if (filterParam === '') { if (filterParam === '') {
return dataParam; return dataParam;
} }
...@@ -217,7 +221,7 @@ export const getParameterByName = (name, urlToParse) => { ...@@ -217,7 +221,7 @@ export const getParameterByName = (name, urlToParse) => {
return decodeURIComponent(results[2].replace(/\+/g, ' ')); return decodeURIComponent(results[2].replace(/\+/g, ' '));
}; };
const handleSelectedRange = (range) => { const handleSelectedRange = range => {
const container = range.commonAncestorContainer; const container = range.commonAncestorContainer;
// add context to fragment if needed // add context to fragment if needed
if (container.tagName === 'OL') { if (container.tagName === 'OL') {
...@@ -514,7 +518,7 @@ export const setFaviconOverlay = overlayPath => { ...@@ -514,7 +518,7 @@ export const setFaviconOverlay = overlayPath => {
); );
}; };
export const setFavicon = (faviconPath) => { export const setFavicon = faviconPath => {
const faviconEl = document.getElementById('favicon'); const faviconEl = document.getElementById('favicon');
if (faviconEl && faviconPath) { if (faviconEl && faviconPath) {
faviconEl.setAttribute('href', faviconPath); faviconEl.setAttribute('href', faviconPath);
...@@ -539,7 +543,7 @@ export const setCiStatusFavicon = pageUrl => ...@@ -539,7 +543,7 @@ export const setCiStatusFavicon = pageUrl =>
} }
return resetFavicon(); return resetFavicon();
}) })
.catch((error) => { .catch(error => {
resetFavicon(); resetFavicon();
throw error; throw error;
}); });
......
...@@ -411,6 +411,22 @@ The following GitLab features are used among others: ...@@ -411,6 +411,22 @@ The following GitLab features are used among others:
Every GitLab instance includes the documentation, which is available from `/help` Every GitLab instance includes the documentation, which is available from `/help`
(`http://my-instance.com/help`), e.g., <https://gitlab.com/help>. (`http://my-instance.com/help`), e.g., <https://gitlab.com/help>.
The documentation available online on docs.gitlab.com is continuously
deployed every hour from the `master` branch of CE, EE, Omnibus, and Runner. Therefore,
once a merge request gets merged, it will be available online on the same day,
but they will be shipped (and available on `/help`) within the milestone assigned
to the MR.
For instance, let's say your merge request has a milestone set to 11.3, which
will be released on 2018-09-22. If it gets merged on 2018-09-15, it will be
available online on 2018-09-15, but, as the feature freeze date has passed, if
the MR does not have a "pick into 11.3" label, the milestone has to be changed
to 11.4 and it will be shipped with all GitLab packages only on 2018-10-22,
with GitLab 11.4. Meaning, it will only be available under `/help` from GitLab
11.4 onwards, but available on docs.gitlab.com on the same day it was merged.
### Linking to `/help`
When you're building a new feature, you may need to link the documentation When you're building a new feature, you may need to link the documentation
from GitLab, the application. This is normally done in files inside the from GitLab, the application. This is normally done in files inside the
`app/views/` directory with the help of the `help_page_path` helper method. `app/views/` directory with the help of the `help_page_path` helper method.
......
...@@ -59,6 +59,12 @@ parallelization, monitoring. ...@@ -59,6 +59,12 @@ parallelization, monitoring.
--- ---
## [Review apps](review_apps.md)
How review apps are set up for GitLab CE/EE and how to use them.
---
## [Testing Rake tasks](testing_rake_tasks.md) ## [Testing Rake tasks](testing_rake_tasks.md)
Everything you should know about how to test Rake tasks. Everything you should know about how to test Rake tasks.
......
# Review apps
We currently have review apps available as a manual job in EE pipelines. Here is
[the first implementation](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/6259).
That said, [the Quality team is working](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/6665)
on making Review Apps automatically deployed by each pipeline, both in CE and EE.
## How does it work?
1. On every EE [pipeline][gitlab-pipeline] during the `test` stage, you can
start the [`review` job][review-job]
1. The `review` job [triggers a pipeline][cng-pipeline] in the
[`CNG-mirror`][cng-mirror] [^1] project
1. The `CNG-mirror` pipeline creates the Docker images of each component (e.g. `gitlab-rails-ee`,
`gitlab-shell`, `gitaly` etc.) based on the commit from the
[GitLab pipeline][gitlab-pipeline] and store them in its
[registry][cng-mirror-registry]
1. Once all images are built, the review app is deployed using
[the official GitLab Helm chart][helm-chart] [^2] to the
[`review-apps-ee` Kubernetes cluster on GCP][review-apps-ee]
- The actual scripts used to deploy the review app can be found at
[`scripts/review_apps/review-apps.sh`][review-apps.sh]
- These scripts are basically
[our official Auto DevOps scripts][Auto-DevOps.gitlab-ci.yml] where the
default CNG images are overriden with the images built and stored in the
[`CNG-mirror` project's registry][cng-mirror-registry]
1. Once the `review` job succeeds, you should be able to use your review app
thanks to the direct link to it from the MR widget. The default username is
`root` and its password can be found in the 1Password secure note named
**gitlab-{ce,ee} review app's root password**.
**Additional notes:**
- The Kubernetes cluster is connected to the `gitlab-ee` project using [GitLab's
Kubernetes integration][gitlab-k8s-integration]. This basically allows to have
a link to the review app directly from the merge request widget.
- The manual `stop_review` in the `post-cleanup` stage can be used to stop a
review app manually, and is also started by GitLab once a branch is deleted
- [TBD] Review apps are cleaned up regularly using a pipeline schedule that runs
the [`scripts/review_apps/automated_cleanup.rb`][automated_cleanup.rb] script
[^1]: We use the `CNG-mirror` project so that the `CNG`, (**C**loud **N**ative **G**itLab), project's registry is
not overloaded with a lot of transient Docker images.
[^2]: Since we're using [the official GitLab Helm chart][helm-chart], this means
you get the a dedicated environment for your branch that's very close to what it
would look in production
## Frequently Asked Questions
**Will it be too much to trigger CNG image builds on every test run? This could create thousands of unused docker images.**
> We have to start somewhere and improve later. If we see this getting out of hand, we will revisit.
**How big is the Kubernetes cluster?**
> The cluster is currently setup with a single pool of preemptible
nodes, with a minimum of 1 node and a maximum of 30 nodes.
**What are the machine running on the cluster?**
> We're currently using `n1-standard-4` (4 vCPUs, 15 GB memory) machines.
**How do we secure this from abuse? Apps are open to the world so we need to find a way to limit it to only us.**
> This won't work for forks. We will add a root password to 1password shared vault.
[gitlab-pipeline]: https://gitlab.com/gitlab-org/gitlab-ee/pipelines/29302122
[review-job]: https://gitlab.com/gitlab-org/gitlab-ee/-/jobs/94294136
[cng-mirror]: https://gitlab.com/gitlab-org/build/CNG-mirror
[cng-pipeline]: https://gitlab.com/gitlab-org/build/CNG-mirror/pipelines/29307727
[cng-mirror-registry]: https://gitlab.com/gitlab-org/build/CNG-mirror/container_registry
[helm-chart]: https://gitlab.com/charts/gitlab/
[review-apps-ee]: https://console.cloud.google.com/kubernetes/clusters/details/us-central1-b/review-apps-ee?project=gitlab-review-apps
[review-apps.sh]: https://gitlab.com/gitlab-org/gitlab-ee/blob/master/scripts/review_apps/review-apps.sh
[automated_cleanup.rb]: https://gitlab.com/gitlab-org/gitlab-ee/blob/master/scripts/review_apps/automated_cleanup.rb
[Auto-DevOps.gitlab-ci.yml]: https://gitlab.com/gitlab-org/gitlab-ci-yml/blob/master/Auto-DevOps.gitlab-ci.yml
[gitlab-k8s-integration]: https://docs.gitlab.com/ee/user/project/clusters/index.html
---
[Return to Testing documentation](index.md)
...@@ -154,7 +154,7 @@ sudo -u git -H make ...@@ -154,7 +154,7 @@ sudo -u git -H make
#### New Gitaly configuration options required #### New Gitaly configuration options required
In order to function Gitaly needs some additional configuration information. Below we assume you installed Gitaly in `/home/git/gitaly` and GitLab Shell in `/home/git/gitlab-shell'. In order to function Gitaly needs some additional configuration information. Below we assume you installed Gitaly in `/home/git/gitaly` and GitLab Shell in `/home/git/gitlab-shell`.
```shell ```shell
echo ' echo '
......
...@@ -154,7 +154,7 @@ sudo -u git -H make ...@@ -154,7 +154,7 @@ sudo -u git -H make
#### New Gitaly configuration options required #### New Gitaly configuration options required
In order to function Gitaly needs some additional configuration information. Below we assume you installed Gitaly in `/home/git/gitaly` and GitLab Shell in `/home/git/gitlab-shell'. In order to function Gitaly needs some additional configuration information. Below we assume you installed Gitaly in `/home/git/gitaly` and GitLab Shell in `/home/git/gitlab-shell`.
```shell ```shell
echo ' echo '
......
...@@ -5,7 +5,23 @@ import { ...@@ -5,7 +5,23 @@ import {
INLINE_DIFF_VIEW_TYPE, INLINE_DIFF_VIEW_TYPE,
PARALLEL_DIFF_VIEW_TYPE, PARALLEL_DIFF_VIEW_TYPE,
} from '~/diffs/constants'; } from '~/diffs/constants';
import * as actions from '~/diffs/store/actions'; import actions, {
setBaseConfig,
fetchDiffFiles,
assignDiscussionsToDiff,
removeDiscussionsFromDiff,
startRenderDiffsQueue,
setInlineDiffViewType,
setParallelDiffViewType,
showCommentForm,
cancelCommentForm,
loadMoreLines,
scrollToLineIfNeededInline,
scrollToLineIfNeededParallel,
loadCollapsedDiff,
expandAllFiles,
toggleFileDiscussions,
} from '~/diffs/store/actions';
import * as types from '~/diffs/store/mutation_types'; import * as types from '~/diffs/store/mutation_types';
import { reduceDiscussionsToLineCodes } from '~/notes/stores/utils'; import { reduceDiscussionsToLineCodes } from '~/notes/stores/utils';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
...@@ -37,7 +53,7 @@ describe('DiffsStoreActions', () => { ...@@ -37,7 +53,7 @@ describe('DiffsStoreActions', () => {
const projectPath = '/root/project'; const projectPath = '/root/project';
testAction( testAction(
actions.setBaseConfig, setBaseConfig,
{ endpoint, projectPath }, { endpoint, projectPath },
{ endpoint: '', projectPath: '' }, { endpoint: '', projectPath: '' },
[{ type: types.SET_BASE_CONFIG, payload: { endpoint, projectPath } }], [{ type: types.SET_BASE_CONFIG, payload: { endpoint, projectPath } }],
...@@ -55,7 +71,7 @@ describe('DiffsStoreActions', () => { ...@@ -55,7 +71,7 @@ describe('DiffsStoreActions', () => {
mock.onGet(endpoint).reply(200, res); mock.onGet(endpoint).reply(200, res);
testAction( testAction(
actions.fetchDiffFiles, fetchDiffFiles,
{}, {},
{ endpoint }, { endpoint },
[ [
...@@ -139,7 +155,7 @@ describe('DiffsStoreActions', () => { ...@@ -139,7 +155,7 @@ describe('DiffsStoreActions', () => {
const discussions = reduceDiscussionsToLineCodes([singleDiscussion]); const discussions = reduceDiscussionsToLineCodes([singleDiscussion]);
testAction( testAction(
actions.assignDiscussionsToDiff, assignDiscussionsToDiff,
discussions, discussions,
state, state,
[ [
...@@ -208,7 +224,7 @@ describe('DiffsStoreActions', () => { ...@@ -208,7 +224,7 @@ describe('DiffsStoreActions', () => {
}; };
testAction( testAction(
actions.removeDiscussionsFromDiff, removeDiscussionsFromDiff,
singleDiscussion, singleDiscussion,
state, state,
[ [
...@@ -252,8 +268,7 @@ describe('DiffsStoreActions', () => { ...@@ -252,8 +268,7 @@ describe('DiffsStoreActions', () => {
}); });
}; };
actions startRenderDiffsQueue({ state, commit: pseudoCommit })
.startRenderDiffsQueue({ state, commit: pseudoCommit })
.then(() => { .then(() => {
expect(state.diffFiles[0].renderIt).toBeTruthy(); expect(state.diffFiles[0].renderIt).toBeTruthy();
expect(state.diffFiles[1].renderIt).toBeTruthy(); expect(state.diffFiles[1].renderIt).toBeTruthy();
...@@ -269,7 +284,7 @@ describe('DiffsStoreActions', () => { ...@@ -269,7 +284,7 @@ describe('DiffsStoreActions', () => {
describe('setInlineDiffViewType', () => { describe('setInlineDiffViewType', () => {
it('should set diff view type to inline and also set the cookie properly', done => { it('should set diff view type to inline and also set the cookie properly', done => {
testAction( testAction(
actions.setInlineDiffViewType, setInlineDiffViewType,
null, null,
{}, {},
[{ type: types.SET_DIFF_VIEW_TYPE, payload: INLINE_DIFF_VIEW_TYPE }], [{ type: types.SET_DIFF_VIEW_TYPE, payload: INLINE_DIFF_VIEW_TYPE }],
...@@ -287,7 +302,7 @@ describe('DiffsStoreActions', () => { ...@@ -287,7 +302,7 @@ describe('DiffsStoreActions', () => {
describe('setParallelDiffViewType', () => { describe('setParallelDiffViewType', () => {
it('should set diff view type to parallel and also set the cookie properly', done => { it('should set diff view type to parallel and also set the cookie properly', done => {
testAction( testAction(
actions.setParallelDiffViewType, setParallelDiffViewType,
null, null,
{}, {},
[{ type: types.SET_DIFF_VIEW_TYPE, payload: PARALLEL_DIFF_VIEW_TYPE }], [{ type: types.SET_DIFF_VIEW_TYPE, payload: PARALLEL_DIFF_VIEW_TYPE }],
...@@ -307,7 +322,7 @@ describe('DiffsStoreActions', () => { ...@@ -307,7 +322,7 @@ describe('DiffsStoreActions', () => {
const payload = { lineCode: 'lineCode' }; const payload = { lineCode: 'lineCode' };
testAction( testAction(
actions.showCommentForm, showCommentForm,
payload, payload,
{}, {},
[{ type: types.ADD_COMMENT_FORM_LINE, payload }], [{ type: types.ADD_COMMENT_FORM_LINE, payload }],
...@@ -322,7 +337,7 @@ describe('DiffsStoreActions', () => { ...@@ -322,7 +337,7 @@ describe('DiffsStoreActions', () => {
const payload = { lineCode: 'lineCode' }; const payload = { lineCode: 'lineCode' };
testAction( testAction(
actions.cancelCommentForm, cancelCommentForm,
payload, payload,
{}, {},
[{ type: types.REMOVE_COMMENT_FORM_LINE, payload }], [{ type: types.REMOVE_COMMENT_FORM_LINE, payload }],
...@@ -344,7 +359,7 @@ describe('DiffsStoreActions', () => { ...@@ -344,7 +359,7 @@ describe('DiffsStoreActions', () => {
mock.onGet(endpoint).reply(200, contextLines); mock.onGet(endpoint).reply(200, contextLines);
testAction( testAction(
actions.loadMoreLines, loadMoreLines,
options, options,
{}, {},
[ [
...@@ -370,7 +385,7 @@ describe('DiffsStoreActions', () => { ...@@ -370,7 +385,7 @@ describe('DiffsStoreActions', () => {
mock.onGet(file.loadCollapsedDiffUrl).reply(200, data); mock.onGet(file.loadCollapsedDiffUrl).reply(200, data);
testAction( testAction(
actions.loadCollapsedDiff, loadCollapsedDiff,
file, file,
{}, {},
[ [
...@@ -391,7 +406,7 @@ describe('DiffsStoreActions', () => { ...@@ -391,7 +406,7 @@ describe('DiffsStoreActions', () => {
describe('expandAllFiles', () => { describe('expandAllFiles', () => {
it('should change the collapsed prop from the diffFiles', done => { it('should change the collapsed prop from the diffFiles', done => {
testAction( testAction(
actions.expandAllFiles, expandAllFiles,
null, null,
{}, {},
[ [
...@@ -415,7 +430,7 @@ describe('DiffsStoreActions', () => { ...@@ -415,7 +430,7 @@ describe('DiffsStoreActions', () => {
const dispatch = jasmine.createSpy('dispatch'); const dispatch = jasmine.createSpy('dispatch');
actions.toggleFileDiscussions({ getters, dispatch }); toggleFileDiscussions({ getters, dispatch });
expect(dispatch).toHaveBeenCalledWith( expect(dispatch).toHaveBeenCalledWith(
'collapseDiscussion', 'collapseDiscussion',
...@@ -433,7 +448,7 @@ describe('DiffsStoreActions', () => { ...@@ -433,7 +448,7 @@ describe('DiffsStoreActions', () => {
const dispatch = jasmine.createSpy(); const dispatch = jasmine.createSpy();
actions.toggleFileDiscussions({ getters, dispatch }); toggleFileDiscussions({ getters, dispatch });
expect(dispatch).toHaveBeenCalledWith( expect(dispatch).toHaveBeenCalledWith(
'expandDiscussion', 'expandDiscussion',
...@@ -451,7 +466,7 @@ describe('DiffsStoreActions', () => { ...@@ -451,7 +466,7 @@ describe('DiffsStoreActions', () => {
const dispatch = jasmine.createSpy(); const dispatch = jasmine.createSpy();
actions.toggleFileDiscussions({ getters, dispatch }); toggleFileDiscussions({ getters, dispatch });
expect(dispatch).toHaveBeenCalledWith( expect(dispatch).toHaveBeenCalledWith(
'expandDiscussion', 'expandDiscussion',
...@@ -460,4 +475,111 @@ describe('DiffsStoreActions', () => { ...@@ -460,4 +475,111 @@ describe('DiffsStoreActions', () => {
); );
}); });
}); });
describe('scrollToLineIfNeededInline', () => {
const lineMock = {
lineCode: 'ABC_123',
};
it('should not call handleLocationHash when there is not hash', () => {
window.location.hash = '';
const handleLocationHashSpy = spyOnDependency(actions, 'handleLocationHash').and.stub();
scrollToLineIfNeededInline({}, lineMock);
expect(handleLocationHashSpy).not.toHaveBeenCalled();
});
it('should not call handleLocationHash when the hash does not match any line', () => {
window.location.hash = 'XYZ_456';
const handleLocationHashSpy = spyOnDependency(actions, 'handleLocationHash').and.stub();
scrollToLineIfNeededInline({}, lineMock);
expect(handleLocationHashSpy).not.toHaveBeenCalled();
});
it('should call handleLocationHash only when the hash matches a line', () => {
window.location.hash = 'ABC_123';
const handleLocationHashSpy = spyOnDependency(actions, 'handleLocationHash').and.stub();
scrollToLineIfNeededInline(
{},
{
lineCode: 'ABC_456',
},
);
scrollToLineIfNeededInline({}, lineMock);
scrollToLineIfNeededInline(
{},
{
lineCode: 'XYZ_456',
},
);
expect(handleLocationHashSpy).toHaveBeenCalled();
expect(handleLocationHashSpy).toHaveBeenCalledTimes(1);
});
});
describe('scrollToLineIfNeededParallel', () => {
const lineMock = {
left: null,
right: {
lineCode: 'ABC_123',
},
};
it('should not call handleLocationHash when there is not hash', () => {
window.location.hash = '';
const handleLocationHashSpy = spyOnDependency(actions, 'handleLocationHash').and.stub();
scrollToLineIfNeededParallel({}, lineMock);
expect(handleLocationHashSpy).not.toHaveBeenCalled();
});
it('should not call handleLocationHash when the hash does not match any line', () => {
window.location.hash = 'XYZ_456';
const handleLocationHashSpy = spyOnDependency(actions, 'handleLocationHash').and.stub();
scrollToLineIfNeededParallel({}, lineMock);
expect(handleLocationHashSpy).not.toHaveBeenCalled();
});
it('should call handleLocationHash only when the hash matches a line', () => {
window.location.hash = 'ABC_123';
const handleLocationHashSpy = spyOnDependency(actions, 'handleLocationHash').and.stub();
scrollToLineIfNeededParallel(
{},
{
left: null,
right: {
lineCode: 'ABC_456',
},
},
);
scrollToLineIfNeededParallel({}, lineMock);
scrollToLineIfNeededParallel(
{},
{
left: null,
right: {
lineCode: 'XYZ_456',
},
},
);
expect(handleLocationHashSpy).toHaveBeenCalled();
expect(handleLocationHashSpy).toHaveBeenCalledTimes(1);
});
});
}); });
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