gitlab-org--gitlab-foss/spec/frontend/captcha/apollo_captcha_link_spec.js

166 lines
4.8 KiB
JavaScript

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();
},
});
});
});
});