summaryrefslogtreecommitdiff
path: root/spec/frontend/lib
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/lib')
-rw-r--r--spec/frontend/lib/apollo/suppress_network_errors_during_navigation_link_spec.js155
-rw-r--r--spec/frontend/lib/logger/__snapshots__/hello_spec.js.snap21
-rw-r--r--spec/frontend/lib/logger/hello_spec.js28
-rw-r--r--spec/frontend/lib/utils/color_utils_spec.js18
-rw-r--r--spec/frontend/lib/utils/datetime/date_format_utility_spec.js15
-rw-r--r--spec/frontend/lib/utils/is_navigating_away_spec.js23
-rw-r--r--spec/frontend/lib/utils/text_utility_spec.js21
-rw-r--r--spec/frontend/lib/utils/url_utility_spec.js35
8 files changed, 310 insertions, 6 deletions
diff --git a/spec/frontend/lib/apollo/suppress_network_errors_during_navigation_link_spec.js b/spec/frontend/lib/apollo/suppress_network_errors_during_navigation_link_spec.js
new file mode 100644
index 00000000000..852106db44e
--- /dev/null
+++ b/spec/frontend/lib/apollo/suppress_network_errors_during_navigation_link_spec.js
@@ -0,0 +1,155 @@
+import { ApolloLink, Observable } from 'apollo-link';
+import waitForPromises from 'helpers/wait_for_promises';
+import { getSuppressNetworkErrorsDuringNavigationLink } from '~/lib/apollo/suppress_network_errors_during_navigation_link';
+import { isNavigatingAway } from '~/lib/utils/is_navigating_away';
+
+jest.mock('~/lib/utils/is_navigating_away');
+
+describe('getSuppressNetworkErrorsDuringNavigationLink', () => {
+ const originalGon = window.gon;
+ let subscription;
+
+ beforeEach(() => {
+ window.gon = originalGon;
+ });
+
+ afterEach(() => {
+ if (subscription) {
+ subscription.unsubscribe();
+ }
+ });
+
+ const makeMockGraphQLErrorLink = () =>
+ new ApolloLink(() =>
+ Observable.of({
+ errors: [
+ {
+ message: 'foo',
+ },
+ ],
+ }),
+ );
+
+ const makeMockNetworkErrorLink = () =>
+ new ApolloLink(
+ () =>
+ new Observable(() => {
+ throw new Error('NetworkError');
+ }),
+ );
+
+ const makeMockSuccessLink = () =>
+ new ApolloLink(() => Observable.of({ data: { foo: { id: 1 } } }));
+
+ const createSubscription = (otherLink, observer) => {
+ const mockOperation = { operationName: 'foo' };
+ const link = getSuppressNetworkErrorsDuringNavigationLink().concat(otherLink);
+ subscription = link.request(mockOperation).subscribe(observer);
+ };
+
+ describe('when disabled', () => {
+ it('returns null', () => {
+ expect(getSuppressNetworkErrorsDuringNavigationLink()).toBe(null);
+ });
+ });
+
+ describe('when enabled', () => {
+ beforeEach(() => {
+ window.gon = { features: { suppressApolloErrorsDuringNavigation: true } };
+ });
+
+ it('returns an ApolloLink', () => {
+ expect(getSuppressNetworkErrorsDuringNavigationLink()).toEqual(expect.any(ApolloLink));
+ });
+
+ describe('suppression case', () => {
+ describe('when navigating away', () => {
+ beforeEach(() => {
+ isNavigatingAway.mockReturnValue(true);
+ });
+
+ describe('given a network error', () => {
+ it('does not forward the error', async () => {
+ const spy = jest.fn();
+
+ createSubscription(makeMockNetworkErrorLink(), {
+ next: spy,
+ error: spy,
+ complete: spy,
+ });
+
+ // It's hard to test for something _not_ happening. The best we can
+ // do is wait a bit to make sure nothing happens.
+ await waitForPromises();
+ expect(spy).not.toHaveBeenCalled();
+ });
+ });
+ });
+ });
+
+ describe('non-suppression cases', () => {
+ describe('when not navigating away', () => {
+ beforeEach(() => {
+ isNavigatingAway.mockReturnValue(false);
+ });
+
+ it('forwards successful requests', (done) => {
+ createSubscription(makeMockSuccessLink(), {
+ next({ data }) {
+ expect(data).toEqual({ foo: { id: 1 } });
+ },
+ error: () => done.fail('Should not happen'),
+ complete: () => done(),
+ });
+ });
+
+ it('forwards GraphQL errors', (done) => {
+ createSubscription(makeMockGraphQLErrorLink(), {
+ next({ errors }) {
+ expect(errors).toEqual([{ message: 'foo' }]);
+ },
+ error: () => done.fail('Should not happen'),
+ complete: () => done(),
+ });
+ });
+
+ it('forwards network errors', (done) => {
+ createSubscription(makeMockNetworkErrorLink(), {
+ next: () => done.fail('Should not happen'),
+ error: (error) => {
+ expect(error.message).toBe('NetworkError');
+ done();
+ },
+ complete: () => done.fail('Should not happen'),
+ });
+ });
+ });
+
+ describe('when navigating away', () => {
+ beforeEach(() => {
+ isNavigatingAway.mockReturnValue(true);
+ });
+
+ it('forwards successful requests', (done) => {
+ createSubscription(makeMockSuccessLink(), {
+ next({ data }) {
+ expect(data).toEqual({ foo: { id: 1 } });
+ },
+ error: () => done.fail('Should not happen'),
+ complete: () => done(),
+ });
+ });
+
+ it('forwards GraphQL errors', (done) => {
+ createSubscription(makeMockGraphQLErrorLink(), {
+ next({ errors }) {
+ expect(errors).toEqual([{ message: 'foo' }]);
+ },
+ error: () => done.fail('Should not happen'),
+ complete: () => done(),
+ });
+ });
+ });
+ });
+ });
+});
diff --git a/spec/frontend/lib/logger/__snapshots__/hello_spec.js.snap b/spec/frontend/lib/logger/__snapshots__/hello_spec.js.snap
index 791ec05befd..0b156049dab 100644
--- a/spec/frontend/lib/logger/__snapshots__/hello_spec.js.snap
+++ b/spec/frontend/lib/logger/__snapshots__/hello_spec.js.snap
@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`~/lib/logger/hello logHello console logs a friendly hello message 1`] = `
+exports[`~/lib/logger/hello logHello when on dot_com console logs a friendly hello message including the careers page 1`] = `
Array [
Array [
"%cWelcome to GitLab!%c
@@ -8,7 +8,24 @@ Array [
Does this page need fixes or improvements? Open an issue or contribute a merge request to help make GitLab more lovable. At GitLab, everyone can contribute!
🤝 Contribute to GitLab: https://about.gitlab.com/community/contribute/
-🔎 Create a new GitLab issue: https://gitlab.com/gitlab-org/gitlab/-/issues/new",
+🔎 Create a new GitLab issue: https://gitlab.com/gitlab-org/gitlab/-/issues/new
+🚀 We like your curiosity! Help us improve GitLab by joining the team: https://about.gitlab.com/jobs/",
+ "padding-top: 0.5em; font-size: 2em;",
+ "padding-bottom: 0.5em;",
+ ],
+]
+`;
+
+exports[`~/lib/logger/hello logHello when on self managed console logs a friendly hello message without including the careers page 1`] = `
+Array [
+ Array [
+ "%cWelcome to GitLab!%c
+
+Does this page need fixes or improvements? Open an issue or contribute a merge request to help make GitLab more lovable. At GitLab, everyone can contribute!
+
+🤝 Contribute to GitLab: https://about.gitlab.com/community/contribute/
+🔎 Create a new GitLab issue: https://gitlab.com/gitlab-org/gitlab/-/issues/new
+",
"padding-top: 0.5em; font-size: 2em;",
"padding-bottom: 0.5em;",
],
diff --git a/spec/frontend/lib/logger/hello_spec.js b/spec/frontend/lib/logger/hello_spec.js
index 39abe0e0dd0..39c1b55313b 100644
--- a/spec/frontend/lib/logger/hello_spec.js
+++ b/spec/frontend/lib/logger/hello_spec.js
@@ -9,12 +9,32 @@ describe('~/lib/logger/hello', () => {
});
describe('logHello', () => {
- it('console logs a friendly hello message', () => {
- expect(consoleLogSpy).not.toHaveBeenCalled();
+ describe('when on dot_com', () => {
+ beforeEach(() => {
+ gon.dot_com = true;
+ });
- logHello();
+ it('console logs a friendly hello message including the careers page', () => {
+ expect(consoleLogSpy).not.toHaveBeenCalled();
- expect(consoleLogSpy.mock.calls).toMatchSnapshot();
+ logHello();
+
+ expect(consoleLogSpy.mock.calls).toMatchSnapshot();
+ });
+ });
+
+ describe('when on self managed', () => {
+ beforeEach(() => {
+ gon.dot_com = false;
+ });
+
+ it('console logs a friendly hello message without including the careers page', () => {
+ expect(consoleLogSpy).not.toHaveBeenCalled();
+
+ logHello();
+
+ expect(consoleLogSpy.mock.calls).toMatchSnapshot();
+ });
});
});
});
diff --git a/spec/frontend/lib/utils/color_utils_spec.js b/spec/frontend/lib/utils/color_utils_spec.js
index c6b88b2957c..87966cf9fba 100644
--- a/spec/frontend/lib/utils/color_utils_spec.js
+++ b/spec/frontend/lib/utils/color_utils_spec.js
@@ -1,4 +1,5 @@
import {
+ isValidColorExpression,
textColorForBackground,
hexToRgb,
validateHexColor,
@@ -72,4 +73,21 @@ describe('Color utils', () => {
},
);
});
+
+ describe('isValidColorExpression', () => {
+ it.each`
+ colorExpression | valid | desc
+ ${'#F00'} | ${true} | ${'valid'}
+ ${'rgba(0,0,0,0)'} | ${true} | ${'valid'}
+ ${'hsl(540,70%,50%)'} | ${true} | ${'valid'}
+ ${'red'} | ${true} | ${'valid'}
+ ${'F00'} | ${false} | ${'invalid'}
+ ${'F00'} | ${false} | ${'invalid'}
+ ${'gba(0,0,0,0)'} | ${false} | ${'invalid'}
+ ${'hls(540,70%,50%)'} | ${false} | ${'invalid'}
+ ${'hello'} | ${false} | ${'invalid'}
+ `('color expression $colorExpression is $desc', ({ colorExpression, valid }) => {
+ expect(isValidColorExpression(colorExpression)).toBe(valid);
+ });
+ });
});
diff --git a/spec/frontend/lib/utils/datetime/date_format_utility_spec.js b/spec/frontend/lib/utils/datetime/date_format_utility_spec.js
index 942ba56196e..1adc70450e8 100644
--- a/spec/frontend/lib/utils/datetime/date_format_utility_spec.js
+++ b/spec/frontend/lib/utils/datetime/date_format_utility_spec.js
@@ -118,3 +118,18 @@ describe('date_format_utility.js', () => {
});
});
});
+
+describe('formatTimeAsSummary', () => {
+ it.each`
+ unit | value | result
+ ${'months'} | ${1.5} | ${'1.5M'}
+ ${'weeks'} | ${1.25} | ${'1.5w'}
+ ${'days'} | ${2} | ${'2d'}
+ ${'hours'} | ${10} | ${'10h'}
+ ${'minutes'} | ${20} | ${'20m'}
+ ${'seconds'} | ${10} | ${'<1m'}
+ ${'seconds'} | ${0} | ${'-'}
+ `('will format $value $unit to $result', ({ unit, value, result }) => {
+ expect(utils.formatTimeAsSummary({ [unit]: value })).toBe(result);
+ });
+});
diff --git a/spec/frontend/lib/utils/is_navigating_away_spec.js b/spec/frontend/lib/utils/is_navigating_away_spec.js
new file mode 100644
index 00000000000..e1230fe96bf
--- /dev/null
+++ b/spec/frontend/lib/utils/is_navigating_away_spec.js
@@ -0,0 +1,23 @@
+import { isNavigatingAway, setNavigatingForTestsOnly } from '~/lib/utils/is_navigating_away';
+
+describe('isNavigatingAway', () => {
+ beforeEach(() => {
+ // Make sure each test starts with the same state
+ setNavigatingForTestsOnly(false);
+ });
+
+ it.each([false, true])('it returns the navigation flag with value %s', (flag) => {
+ setNavigatingForTestsOnly(flag);
+ expect(isNavigatingAway()).toEqual(flag);
+ });
+
+ describe('when the browser starts navigating away', () => {
+ it('returns true', () => {
+ expect(isNavigatingAway()).toEqual(false);
+
+ window.dispatchEvent(new Event('beforeunload'));
+
+ expect(isNavigatingAway()).toEqual(true);
+ });
+ });
+});
diff --git a/spec/frontend/lib/utils/text_utility_spec.js b/spec/frontend/lib/utils/text_utility_spec.js
index 1f3659b5c76..9570d2a831c 100644
--- a/spec/frontend/lib/utils/text_utility_spec.js
+++ b/spec/frontend/lib/utils/text_utility_spec.js
@@ -363,4 +363,25 @@ describe('text_utility', () => {
expect(textUtils.insertFinalNewline(input, '\r\n')).toBe(output);
});
});
+
+ describe('escapeShellString', () => {
+ it.each`
+ character | input | output
+ ${'"'} | ${'";echo "you_shouldnt_run_this'} | ${'\'";echo "you_shouldnt_run_this\''}
+ ${'$'} | ${'$IFS'} | ${"'$IFS'"}
+ ${'\\'} | ${'evil-branch-name\\'} | ${"'evil-branch-name\\'"}
+ ${'!'} | ${'!event'} | ${"'!event'"}
+ `(
+ 'should not escape the $character character but wrap in single-quotes',
+ ({ input, output }) => {
+ expect(textUtils.escapeShellString(input)).toBe(output);
+ },
+ );
+
+ it("should escape the ' character and wrap in single-quotes", () => {
+ expect(textUtils.escapeShellString("fix-'bug-behavior'")).toBe(
+ "'fix-'\\''bug-behavior'\\'''",
+ );
+ });
+ });
});
diff --git a/spec/frontend/lib/utils/url_utility_spec.js b/spec/frontend/lib/utils/url_utility_spec.js
index 6f186ba3227..18b68d91e01 100644
--- a/spec/frontend/lib/utils/url_utility_spec.js
+++ b/spec/frontend/lib/utils/url_utility_spec.js
@@ -1004,4 +1004,39 @@ describe('URL utility', () => {
expect(urlUtils.isSameOriginUrl(url)).toBe(expected);
});
});
+
+ describe('constructWebIDEPath', () => {
+ let originalGl;
+ const projectIDEPath = '/foo/bar';
+ const sourceProj = 'my_-fancy-proj/boo';
+ const targetProj = 'boo/another-fancy-proj';
+ const mrIid = '7';
+
+ beforeEach(() => {
+ originalGl = window.gl;
+ window.gl = { webIDEPath: projectIDEPath };
+ });
+
+ afterEach(() => {
+ window.gl = originalGl;
+ });
+
+ it.each`
+ sourceProjectFullPath | targetProjectFullPath | iid | expectedPath
+ ${undefined} | ${undefined} | ${undefined} | ${projectIDEPath}
+ ${undefined} | ${undefined} | ${mrIid} | ${projectIDEPath}
+ ${undefined} | ${targetProj} | ${undefined} | ${projectIDEPath}
+ ${undefined} | ${targetProj} | ${mrIid} | ${projectIDEPath}
+ ${sourceProj} | ${undefined} | ${undefined} | ${projectIDEPath}
+ ${sourceProj} | ${targetProj} | ${undefined} | ${projectIDEPath}
+ ${sourceProj} | ${undefined} | ${mrIid} | ${`/-/ide/project/${sourceProj}/merge_requests/${mrIid}?target_project=`}
+ ${sourceProj} | ${sourceProj} | ${mrIid} | ${`/-/ide/project/${sourceProj}/merge_requests/${mrIid}?target_project=`}
+ ${sourceProj} | ${targetProj} | ${mrIid} | ${`/-/ide/project/${sourceProj}/merge_requests/${mrIid}?target_project=${encodeURIComponent(targetProj)}`}
+ `(
+ 'returns $expectedPath for "$sourceProjectFullPath + $targetProjectFullPath + $iid"',
+ ({ expectedPath, ...args } = {}) => {
+ expect(urlUtils.constructWebIDEPath(args)).toBe(expectedPath);
+ },
+ );
+ });
});