summaryrefslogtreecommitdiff
path: root/spec/frontend/captcha/apollo_captcha_link_spec.js
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/captcha/apollo_captcha_link_spec.js')
-rw-r--r--spec/frontend/captcha/apollo_captcha_link_spec.js165
1 files changed, 165 insertions, 0 deletions
diff --git a/spec/frontend/captcha/apollo_captcha_link_spec.js b/spec/frontend/captcha/apollo_captcha_link_spec.js
new file mode 100644
index 00000000000..e7ff4812ee7
--- /dev/null
+++ b/spec/frontend/captcha/apollo_captcha_link_spec.js
@@ -0,0 +1,165 @@
+import { ApolloLink, Observable } from 'apollo-link';
+
+import { apolloCaptchaLink } from '~/captcha/apollo_captcha_link';
+import UnsolvedCaptchaError from '~/captcha/unsolved_captcha_error';
+import { waitForCaptchaToBeSolved } from '~/captcha/wait_for_captcha_to_be_solved';
+
+jest.mock('~/captcha/wait_for_captcha_to_be_solved');
+
+describe('apolloCaptchaLink', () => {
+ const SPAM_LOG_ID = 'SPAM_LOG_ID';
+ const CAPTCHA_SITE_KEY = 'CAPTCHA_SITE_KEY';
+ const CAPTCHA_RESPONSE = 'CAPTCHA_RESPONSE';
+
+ const SUCCESS_RESPONSE = {
+ data: {
+ user: {
+ id: 3,
+ name: 'foo',
+ },
+ },
+ errors: [],
+ };
+
+ const NON_CAPTCHA_ERROR_RESPONSE = {
+ data: {
+ user: null,
+ },
+ errors: [
+ {
+ message: 'Something is severely wrong with your query.',
+ path: ['user'],
+ locations: [{ line: 2, column: 3 }],
+ extensions: {
+ message: 'Object not found',
+ type: 2,
+ },
+ },
+ ],
+ };
+
+ const SPAM_ERROR_RESPONSE = {
+ data: {
+ user: null,
+ },
+ errors: [
+ {
+ message: 'Your Query was detected to be spam.',
+ path: ['user'],
+ locations: [{ line: 2, column: 3 }],
+ extensions: {
+ spam: true,
+ },
+ },
+ ],
+ };
+
+ const CAPTCHA_ERROR_RESPONSE = {
+ data: {
+ user: null,
+ },
+ errors: [
+ {
+ message: 'This is an unrelated error, captcha should still work despite this.',
+ path: ['user'],
+ locations: [{ line: 2, column: 3 }],
+ },
+ {
+ message: 'You need to solve a Captcha.',
+ path: ['user'],
+ locations: [{ line: 2, column: 3 }],
+ extensions: {
+ spam: true,
+ needs_captcha_response: true,
+ captcha_site_key: CAPTCHA_SITE_KEY,
+ spam_log_id: SPAM_LOG_ID,
+ },
+ },
+ ],
+ };
+
+ let link;
+
+ let mockLinkImplementation;
+ let mockContext;
+
+ const setupLink = (...responses) => {
+ mockLinkImplementation = jest.fn().mockImplementation(() => {
+ return Observable.of(responses.shift());
+ });
+ link = ApolloLink.from([apolloCaptchaLink, new ApolloLink(mockLinkImplementation)]);
+ };
+
+ function mockOperation() {
+ mockContext = jest.fn();
+ return { operationName: 'operation', variables: {}, setContext: mockContext };
+ }
+
+ it('successful responses are passed through', (done) => {
+ setupLink(SUCCESS_RESPONSE);
+ link.request(mockOperation()).subscribe((result) => {
+ expect(result).toEqual(SUCCESS_RESPONSE);
+ expect(mockLinkImplementation).toHaveBeenCalledTimes(1);
+ expect(waitForCaptchaToBeSolved).not.toHaveBeenCalled();
+ done();
+ });
+ });
+
+ it('non-spam related errors are passed through', (done) => {
+ setupLink(NON_CAPTCHA_ERROR_RESPONSE);
+ link.request(mockOperation()).subscribe((result) => {
+ expect(result).toEqual(NON_CAPTCHA_ERROR_RESPONSE);
+ expect(mockLinkImplementation).toHaveBeenCalledTimes(1);
+ expect(mockContext).not.toHaveBeenCalled();
+ expect(waitForCaptchaToBeSolved).not.toHaveBeenCalled();
+ done();
+ });
+ });
+
+ it('unresolvable spam errors are passed through', (done) => {
+ setupLink(SPAM_ERROR_RESPONSE);
+ link.request(mockOperation()).subscribe((result) => {
+ expect(result).toEqual(SPAM_ERROR_RESPONSE);
+ expect(mockLinkImplementation).toHaveBeenCalledTimes(1);
+ expect(mockContext).not.toHaveBeenCalled();
+ expect(waitForCaptchaToBeSolved).not.toHaveBeenCalled();
+ done();
+ });
+ });
+
+ describe('resolvable spam errors', () => {
+ it('re-submits request with spam headers if the captcha modal was solved correctly', (done) => {
+ waitForCaptchaToBeSolved.mockResolvedValue(CAPTCHA_RESPONSE);
+ setupLink(CAPTCHA_ERROR_RESPONSE, SUCCESS_RESPONSE);
+ link.request(mockOperation()).subscribe((result) => {
+ expect(result).toEqual(SUCCESS_RESPONSE);
+ expect(waitForCaptchaToBeSolved).toHaveBeenCalledWith(CAPTCHA_SITE_KEY);
+ expect(mockContext).toHaveBeenCalledWith({
+ headers: {
+ 'X-GitLab-Captcha-Response': CAPTCHA_RESPONSE,
+ 'X-GitLab-Spam-Log-Id': SPAM_LOG_ID,
+ },
+ });
+ expect(mockLinkImplementation).toHaveBeenCalledTimes(2);
+ done();
+ });
+ });
+
+ it('throws error if the captcha modal was not solved correctly', (done) => {
+ const error = new UnsolvedCaptchaError();
+ waitForCaptchaToBeSolved.mockRejectedValue(error);
+
+ setupLink(CAPTCHA_ERROR_RESPONSE, SUCCESS_RESPONSE);
+ link.request(mockOperation()).subscribe({
+ next: done.catch,
+ error: (result) => {
+ expect(result).toEqual(error);
+ expect(waitForCaptchaToBeSolved).toHaveBeenCalledWith(CAPTCHA_SITE_KEY);
+ expect(mockContext).not.toHaveBeenCalled();
+ expect(mockLinkImplementation).toHaveBeenCalledTimes(1);
+ done();
+ },
+ });
+ });
+ });
+});