Best JavaScript code snippet using testcafe
index.test.js
Source:index.test.js
1'use strict';2const { assert } = require('chai');3const mockery = require('mockery');4const sinon = require('sinon');5const scmContext = 'gitlab:gitlab.com';6const scmUri = 'gitlab.com:repoId:branchName';7const testCommands = require('./data/commands.json');8const testPrCommands = require('./data/prCommands.json');9const testPrComment = require('./data/gitlab.merge_request.comment.json');10const testPrComments = require('./data/gitlab.merge_request.comments.json');11const testCustomPrCommands = require('./data/customPrCommands.json');12const testRootDirCommands = require('./data/rootDirCommands.json');13const testChildCommands = require('./data/childCommands.json');14const testPayloadOpen = require('./data/gitlab.merge_request.opened.json');15const testPayloadClose = require('./data/gitlab.merge_request.closed.json');16const testPayloadPush = require('./data/gitlab.push.json');17const testCommit = require('./data/gitlab.commit.json');18const testChangedFiles = require('./data/gitlab.merge_request.changedFiles.json');19const testMergeRequest = require('./data/gitlab.merge_request.json');20const testWebhookConfigOpen = require('./data/webhookConfig.merge_request.opened.json');21const testWebhookConfigPushBadHead = require('./data/webhookConfig.push.bad.json');22const testWebhookConfigPush = require('./data/webhookConfig.push.json');23const token = 'myAccessToken';24const commentUserToken = 'commentUserToken';25const prefixUrl = 'https://gitlab.com/api/v4';26sinon.assert.expose(assert, { prefix: '' });27describe('index', function() {28 // Time not important. Only life important.29 this.timeout(5000);30 let GitlabScm;31 let scm;32 let requestMock;33 before(() => {34 mockery.enable({35 useCleanCache: true,36 warnOnUnregistered: false37 });38 });39 beforeEach(() => {40 requestMock = sinon.stub();41 mockery.registerMock('screwdriver-request', requestMock);42 /* eslint-disable global-require */43 GitlabScm = require('../index');44 /* eslint-enable global-require */45 scm = new GitlabScm({46 fusebox: {47 retry: {48 minTimeout: 149 }50 },51 oauthClientId: 'myclientid',52 oauthClientSecret: 'myclientsecret',53 commentUserToken54 });55 });56 afterEach(() => {57 mockery.deregisterAll();58 mockery.resetCache();59 });60 after(() => {61 mockery.disable();62 });63 describe('constructor', () => {64 it('validates input', () => {65 try {66 scm = new GitlabScm();67 assert.fail('should not get here');68 } catch (err) {69 assert.instanceOf(err, Error);70 assert.equal(err.name, 'ValidationError');71 }72 });73 it('constructs successfully', () => {74 const testScm = new GitlabScm({75 oauthClientId: 'myclientid',76 oauthClientSecret: 'myclientsecret',77 username: 'abcd',78 email: 'dev-null@my.email.com'79 });80 assert.deepEqual(testScm.config, {81 oauthClientId: 'myclientid',82 oauthClientSecret: 'myclientsecret',83 username: 'abcd',84 email: 'dev-null@my.email.com',85 gitlabHost: 'gitlab.com',86 gitlabProtocol: 'https',87 fusebox: {},88 readOnly: {},89 https: false90 });91 });92 });93 describe('parseUrl', () => {94 const apiUrl = 'projects/batman%2Ftest';95 let fakeResponse;96 let expectedOptions;97 let expected;98 beforeEach(() => {99 fakeResponse = {100 statusCode: 200,101 body: {102 id: '12345',103 default_branch: 'main'104 }105 };106 expectedOptions = {107 url: `${prefixUrl}/${apiUrl}`,108 method: 'GET',109 context: {110 token111 }112 };113 expected = 'gitlab.com:12345:master';114 requestMock.resolves(fakeResponse);115 });116 it('resolves to the correct parsed url for ssh', () =>117 scm118 .parseUrl({119 checkoutUrl: 'git@gitlab.com:batman/test.git#master',120 token,121 scmContext122 })123 .then(parsed => {124 assert.calledWith(requestMock, expectedOptions);125 assert.equal(parsed, expected);126 }));127 it('resolves to the correct parsed url for ssh with default branch', () => {128 expected = 'gitlab.com:12345:main';129 return scm130 .parseUrl({131 checkoutUrl: 'git@gitlab.com:batman/test.git',132 token,133 scmContext134 })135 .then(parsed => {136 assert.calledWith(requestMock, expectedOptions);137 assert.equal(parsed, expected);138 });139 });140 it('resolves to the correct parsed url for rootDir', () => {141 expected = 'gitlab.com:12345:branch:path/to/source';142 return scm143 .parseUrl({144 checkoutUrl: 'git@gitlab.com:batman/test.git#branch:path/to/source',145 token,146 scmContext147 })148 .then(parsed => {149 assert.calledWith(requestMock, expectedOptions);150 assert.equal(parsed, expected);151 });152 });153 it('resolves to the correct parsed url for https', () => {154 expected = 'gitlab.com:12345:mynewbranch';155 return scm156 .parseUrl({157 checkoutUrl: 'https://batman@gitlab.com/batman/test.git#mynewbranch',158 token,159 scmContext160 })161 .then(parsed => {162 assert.calledWith(requestMock, expectedOptions);163 assert.equal(parsed, expected);164 });165 });166 it('rejects if request fails', () => {167 const err = new Error('Gitlab API error');168 requestMock.rejects(err);169 return scm170 .parseUrl({171 checkoutUrl: 'https://batman@gitlab.com/batman/test.git#mynewbranch',172 token,173 scmContext174 })175 .then(() => assert.fail('Should not get here'))176 .catch(error => {177 assert.calledWith(requestMock, expectedOptions);178 assert.deepEqual(error, err);179 });180 });181 it('rejects if status code is 404', () => {182 const err = new Error('404 Reason "404 Project Not Found" Caller "_parseUrl"');183 err.status = 404;184 requestMock.rejects(err);185 return scm186 .parseUrl({187 checkoutUrl: 'https://batman@gitlab.com/batman/test.git#mynewbranch',188 token,189 scmContext190 })191 .then(() => assert.fail('Should not get here'))192 .catch(error => {193 assert.calledWith(requestMock, expectedOptions);194 assert.match(error.message, '404 Reason "404 Project Not Found" Caller "_parseUrl"');195 assert.match(error.status, 404);196 });197 });198 it('rejects if status code is not 200 & 404', () => {199 const err = new Error('500 Reason "Internal Server Error" Caller "_parseUrl"');200 err.status = 500;201 requestMock.rejects(err);202 return scm203 .parseUrl({204 checkoutUrl: 'https://batman@gitlab.com/batman/test.git#mynewbranch',205 token,206 scmContext207 })208 .then(() => assert.fail('Should not get here'))209 .catch(error => {210 assert.match(error.message, '500 Reason "Internal Server Error" Caller "_parseUrl"');211 assert.match(error.status, 500);212 });213 });214 it('rejects when passed checkoutUrl of another host', () => {215 const expectedError = 'This checkoutUrl is not supported for your current login host.';216 return scm217 .parseUrl({218 checkoutUrl: 'git@gitlab.corp.jp:batman/test.git#master',219 scmContext,220 token221 })222 .then(223 () => {224 assert.fail('Should not get here');225 },226 error => {227 assert.match(error.message, expectedError);228 }229 );230 });231 });232 describe('parseHook', () => {233 const checkoutUrl = 'git@example.com:bdangit/quickstart-generic.git';234 it('resolves the correct parsed config for opened PR', () => {235 const expected = {236 type: 'pr',237 action: 'opened',238 username: 'bdangit',239 checkoutUrl,240 branch: 'master',241 sha: '249b26f2278c39f9efc55986f845dd98ae011763',242 prNum: 6,243 prRef: 'merge_requests/6',244 prSource: 'branch',245 prTitle: 'fix tabby cat',246 ref: 'pull/6/merge',247 hookId: '',248 scmContext249 };250 const headers = {251 'content-type': 'application/json',252 'x-gitlab-event': 'Merge Request Hook'253 };254 return scm.parseHook(headers, testPayloadOpen).then(result => assert.deepEqual(result, expected));255 });256 it('resolves the correct parsed config for closed PR after merged', () => {257 const expected = {258 type: 'pr',259 action: 'closed',260 username: 'bdangit',261 checkoutUrl,262 branch: 'master',263 sha: 'bc2b3a48a428ed23e15960e8d703bf7e3a8a4f54',264 prNum: 2,265 prRef: 'merge_requests/2',266 prSource: 'branch',267 prTitle: 'Fix this stuff',268 ref: 'pull/2/merge',269 hookId: '',270 scmContext271 };272 const headers = {273 'content-type': 'application/json',274 'x-gitlab-event': 'Merge Request Hook'275 };276 return scm.parseHook(headers, testPayloadClose).then(result => assert.deepEqual(result, expected));277 });278 it('resolves the correct parsed config for closed PR after declined', () => {279 const expected = {280 type: 'pr',281 action: 'closed',282 username: 'bdangit',283 checkoutUrl,284 branch: 'master',285 sha: 'bc2b3a48a428ed23e15960e8d703bf7e3a8a4f54',286 prNum: 2,287 prRef: 'merge_requests/2',288 prSource: 'branch',289 prTitle: 'Fix this stuff',290 ref: 'pull/2/merge',291 hookId: '',292 scmContext293 };294 const headers = {295 'content-type': 'application/json',296 'x-gitlab-event': 'Merge Request Hook'297 };298 return scm.parseHook(headers, testPayloadClose).then(result => assert.deepEqual(result, expected));299 });300 it('resolves the correct parsed config for push to repo event', () => {301 const expected = {302 type: 'repo',303 action: 'push',304 username: 'jsmith',305 checkoutUrl: 'git@example.com:mike/diaspora.git',306 commitAuthors: ['Jordi Mallach', 'GitLab dev user'],307 branch: 'master',308 lastCommitMessage: 'fixed readme',309 ref: 'refs/heads/master',310 sha: 'da1560886d4f094c3e6c9ef40349f7d38b5d27d7',311 hookId: '',312 scmContext,313 addedFiles: ['CHANGELOG'],314 modifiedFiles: ['app/controller/application.rb'],315 removedFiles: []316 };317 const headers = {318 'content-type': 'application/json',319 'x-gitlab-event': 'Push Hook'320 };321 return scm.parseHook(headers, testPayloadPush).then(result => assert.deepEqual(result, expected));322 });323 it('resolves null if events are not supported: repoFork', () => {324 const repoFork = {325 'x-event-key': 'repo:fork'326 };327 return scm.parseHook(repoFork, {}).then(result => assert.deepEqual(result, null));328 });329 it('resolves null if events are not supported: prComment', () => {330 const prComment = {331 'x-event-key': 'pullrequest:comment_created'332 };333 return scm.parseHook(prComment, {}).then(result => assert.deepEqual(result, null));334 });335 it('resolves null if events are not supported: issueCreated', () => {336 const issueCreated = {337 'x-event-key': 'issue:created'338 };339 return scm.parseHook(issueCreated, {}).then(result => assert.deepEqual(result, null));340 });341 it('resolves null for a pull request payload with an unsupported action', () => {342 const testHeaders = {343 'x-gitlab-event': 'Push Hook',344 action: 'locked'345 };346 return scm.parseHook(testHeaders, { object_kind: 'merge_request' }).then(result => assert.isNull(result));347 });348 });349 describe('decorateAuthor', () => {350 const apiUrl = 'users';351 const expectedOptions = {352 url: `${prefixUrl}/${apiUrl}`,353 method: 'GET',354 context: {355 token356 },357 searchParams: {358 username: 'batman'359 }360 };361 let fakeResponse;362 beforeEach(() => {363 fakeResponse = {364 statusCode: 200,365 body: [366 {367 username: 'batman',368 name: 'Batman',369 id: 12345,370 state: 'active',371 avatar_url: 'https://gitlab.com/uploads/user/avatar/12345/avatar.png',372 web_url: 'https://gitlab.com/batman'373 }374 ]375 };376 requestMock.resolves(fakeResponse);377 });378 it('resolves to correct decorated author', () => {379 const expected = {380 url: 'https://gitlab.com/batman',381 name: 'Batman',382 username: 'batman',383 avatar: 'https://gitlab.com/uploads/user/avatar/12345/avatar.png'384 };385 return scm386 .decorateAuthor({387 username: 'batman',388 scmContext,389 token390 })391 .then(decorated => {392 assert.calledWith(requestMock, expectedOptions);393 assert.deepEqual(decorated, expected);394 });395 });396 it('rejects if status code is not 200', () => {397 const err = new Error('404 Reason "Resource not found" Caller "_decorateAuthor"');398 err.status = 404;399 requestMock.rejects(err);400 return scm401 .decorateAuthor({402 username: 'batman',403 scmContext,404 token405 })406 .then(() => {407 assert.fail('Should not get here');408 })409 .catch(error => {410 assert.calledWith(requestMock, expectedOptions);411 assert.match(error.message, '404 Reason "Resource not found" Caller "_decorateAuthor"');412 assert.match(error.status, 404);413 });414 });415 it('rejects if fails', () => {416 const err = new Error('500 Reason "Internal Server Error" Caller "_decorateAuthor"');417 err.status = 500;418 requestMock.rejects(err);419 return scm420 .decorateAuthor({421 username: 'batman',422 scmContext,423 token424 })425 .then(() => {426 assert.fail('Should not get here');427 })428 .catch(error => {429 assert.equal(error, err);430 });431 });432 });433 describe('decorateUrl', () => {434 const apiUrl = 'projects/repoId';435 let fakeResponse;436 let expectedOptions;437 beforeEach(() => {438 fakeResponse = {439 statusCode: 200,440 body: {441 path_with_namespace: 'username/repoName'442 }443 };444 expectedOptions = {445 url: `${prefixUrl}/${apiUrl}`,446 method: 'GET',447 context: {448 token449 }450 };451 requestMock.resolves(fakeResponse);452 });453 it('resolves to correct decorated url object', () => {454 const expected = {455 url: 'https://gitlab.com/username/repoName/-/tree/branchName',456 name: 'username/repoName',457 branch: 'branchName',458 rootDir: ''459 };460 return scm461 .decorateUrl({462 scmUri,463 token,464 scmContext465 })466 .then(decorated => {467 assert.calledWith(requestMock, expectedOptions);468 assert.deepEqual(decorated, expected);469 });470 });471 it('resolves to correct decorated url object with rootDir', () => {472 const expected = {473 url: 'https://gitlab.com/username/repoName/-/tree/branchName/path/to/source',474 name: 'username/repoName',475 branch: 'branchName',476 rootDir: 'path/to/source'477 };478 return scm479 .decorateUrl({480 scmUri: 'gitlab.com:repoId:branchName:path/to/source',481 token,482 scmContext483 })484 .then(decorated => {485 assert.calledWith(requestMock, expectedOptions);486 assert.deepEqual(decorated, expected);487 });488 });489 it('rejects if status code is not 200', () => {490 const err = new Error('404 Reason "Resource not found" Caller "lookupScmUri"');491 err.status = 404;492 requestMock.rejects(err);493 return scm494 .decorateUrl({495 scmUri,496 token,497 scmContext498 })499 .then(() => {500 assert.fail('Should not get here');501 })502 .catch(error => {503 assert.calledWith(requestMock, expectedOptions);504 assert.match(error.message, '404 Reason "Resource not found" Caller "lookupScmUri"');505 assert.match(error.status, 404);506 });507 });508 it('rejects when scm settings is mismatch', () => {509 const scmUriNotMatch = 'notMatching.com:repoId:branchName';510 const [scmHost] = scmUriNotMatch.split(':');511 const loginContext = scm.getScmContexts();512 const loginHost = loginContext[0].split(':')[1];513 return scm514 .decorateUrl({515 scmUri: scmUriNotMatch,516 token,517 scmContext518 })519 .then(() => {520 assert.fail('Should not get here');521 })522 .catch(error => {523 assert.match(524 error.message,525 `Pipeline's scmHost ${scmHost} does not match with user's scmHost ${loginHost}`526 );527 });528 });529 it('rejects if fails', () => {530 const err = new Error('Gitlab API error');531 err.status = 500;532 requestMock.rejects(err);533 return scm534 .decorateUrl({535 scmUri,536 token,537 scmContext538 })539 .then(() => {540 assert.fail('Should not get here');541 })542 .catch(error => {543 assert.called(requestMock);544 assert.equal(error, err);545 });546 });547 });548 describe('decorateCommit', () => {549 const sha = '1111111111111111111111111111111111111111';550 const lookupScmUriRoute = 'projects/repoId';551 const commitLookupRoute = `projects/owner%2FrepoName/repository/commits/${sha}`;552 let lookupScmUri;553 let lookupScmUriResponse;554 let commitLookup;555 let commitLookupResponse;556 beforeEach(() => {557 lookupScmUri = {558 url: `${prefixUrl}/${lookupScmUriRoute}`,559 method: 'GET',560 context: {561 token562 }563 };564 lookupScmUriResponse = {565 statusCode: 200,566 body: {567 path_with_namespace: 'owner/repoName'568 }569 };570 commitLookup = {571 url: `${prefixUrl}/${commitLookupRoute}`,572 method: 'GET',573 context: {574 token575 }576 };577 commitLookupResponse = {578 statusCode: 200,579 body: testCommit580 };581 requestMock.onFirstCall().resolves(lookupScmUriResponse);582 requestMock.onSecondCall().resolves(commitLookupResponse);583 });584 it('resolves to correct decorated object', () => {585 const expected = {586 author: {587 url: 'https://cd.screwdriver.cd/',588 name: 'randx',589 username: 'n/a',590 avatar: 'https://cd.screwdriver.cd/assets/unknown_user.png'591 },592 committer: {593 avatar: 'https://cd.screwdriver.cd/assets/unknown_user.png',594 name: 'Dmitriy',595 url: 'https://cd.screwdriver.cd/',596 username: 'n/a'597 },598 message: 'Sanitize for network graph',599 // eslint-disable-next-line600 url: 'https://gitlab.example.com/thedude/gitlab-foss/-/commit/6104942438c14ec7bd21c6cd5bd995272b3faff6'601 };602 return scm603 .decorateCommit({604 sha,605 scmUri,606 token,607 scmContext608 })609 .then(decorated => {610 assert.calledWith(requestMock.firstCall, lookupScmUri);611 assert.calledWith(requestMock.secondCall, commitLookup);612 assert.calledTwice(requestMock);613 assert.deepEqual(decorated, expected);614 });615 });616 it('rejects if status code is not 200', () => {617 const err = new Error('404 Reason "Resource not found" Caller "_decorateCommit: commitLookup"');618 err.status = 404;619 requestMock.onFirstCall().resolves(lookupScmUriResponse);620 requestMock.onSecondCall().rejects(err);621 return scm622 .decorateCommit({623 sha,624 scmUri,625 token,626 scmContext627 })628 .then(() => {629 assert.fail('Should not get here');630 })631 .catch(error => {632 // assert.calledTwice(requestMock);633 assert.match(634 error.message,635 '404 Reason "Resource not found" Caller "_decorateCommit: commitLookup"'636 );637 assert.match(error.status, 404);638 });639 });640 });641 describe('getCommitSha', () => {642 const apiUrl = 'projects/repoId/repository/branches/branchName';643 const expectedOptions = {644 url: `${prefixUrl}/${apiUrl}`,645 method: 'GET',646 context: {647 token648 }649 };650 let fakeResponse;651 beforeEach(() => {652 fakeResponse = {653 statusCode: 200,654 body: {655 commit: {656 id: 'hashValue'657 }658 }659 };660 requestMock.resolves(fakeResponse);661 });662 it('resolves to correct commit sha', () =>663 scm664 .getCommitSha({665 scmUri,666 token,667 scmContext668 })669 .then(sha => {670 assert.calledWith(requestMock, expectedOptions);671 assert.deepEqual(sha, 'hashValue');672 }));673 it('rejects if status code is not 200', () => {674 const err = new Error('404 Reason "Resource not found" Caller "_getCommitSha"');675 err.status = 404;676 requestMock.rejects(err);677 return scm678 .getCommitSha({679 scmUri,680 token,681 scmContext682 })683 .then(() => {684 assert.fail('Should not get here');685 })686 .catch(error => {687 assert.calledWith(requestMock, expectedOptions);688 assert.match(error.message, '404 Reason "Resource not found" Caller "_getCommitSha"');689 assert.match(error.status, 404);690 });691 });692 it('rejects if fails', () => {693 const err = new Error('Gitlab API error');694 requestMock.rejects(err);695 return scm696 .getCommitSha({697 scmUri,698 token,699 scmContext700 })701 .then(() => {702 assert.fail('Should not get here');703 })704 .catch(error => {705 assert.calledWith(requestMock, expectedOptions);706 assert.equal(error, err);707 });708 });709 });710 describe('addPrComment', () => {711 const apiUrl = 'projects/repoId/merge_requests/12345/notes';712 const comment = 'this is a merge request comment';713 const prNum = 12345;714 const expectedOptions = {715 url: `${prefixUrl}/${apiUrl}`,716 method: 'POST',717 context: {718 token: commentUserToken719 },720 json: {721 body: comment722 }723 };724 let fakeResponse;725 let fakeCommentsResponse;726 beforeEach(() => {727 fakeResponse = {728 statusCode: 200,729 body: testPrComment730 };731 fakeCommentsResponse = {732 statusCode: 200,733 body: testPrComments734 };735 requestMock.onFirstCall().resolves({736 statusCode: 200,737 body: []738 });739 requestMock.onSecondCall().resolves(fakeResponse);740 });741 it('resolves to correct PR metadata', () =>742 scm743 .addPrComment({744 comment,745 prNum,746 scmUri,747 token,748 scmContext749 })750 .then(result => {751 assert.calledWith(requestMock.secondCall, expectedOptions);752 assert.deepEqual(result, {753 commentId: 126861726,754 createTime: '2018-12-21T20:33:33.157Z',755 username: 'tkyi'756 });757 }));758 it('resolves to correct PR metadata for edited comment', () => {759 requestMock.onFirstCall().resolves(fakeCommentsResponse);760 requestMock.onSecondCall().resolves(fakeResponse);761 return scm762 .addPrComment({763 comment,764 prNum,765 scmUri,766 token,767 scmContext768 })769 .then(result => {770 assert.calledWith(requestMock.firstCall, {771 url: `${prefixUrl}/${apiUrl}`,772 method: 'GET',773 context: {774 token: commentUserToken775 }776 });777 assert.calledWith(requestMock.secondCall, {778 url: `${prefixUrl}/${apiUrl}/575311268`,779 method: 'PUT',780 context: {781 token: commentUserToken782 },783 json: {784 body: 'this is a merge request comment'785 }786 });787 assert.deepEqual(result, {788 commentId: 126861726,789 createTime: '2018-12-21T20:33:33.157Z',790 username: 'tkyi'791 });792 });793 });794 it('resolves null if status code is not 200', () => {795 fakeResponse = {796 statusCode: 404,797 body: {798 message: 'Resource not found'799 }800 };801 requestMock.onSecondCall().resolves(fakeResponse);802 return scm803 .addPrComment({804 comment,805 prNum,806 scmUri,807 token,808 scmContext809 })810 .then(data => {811 assert.isNull(data);812 })813 .catch(error => {814 assert.calledWith(requestMock, expectedOptions);815 assert.match(error.message, '404 Reason "Resource not found" Caller "_addPrComment"');816 assert.match(error.status, 404);817 });818 });819 it('resolves null if status code is not 200 for edited comment', () => {820 fakeResponse = {821 statusCode: 404,822 body: {823 message: 'Resource not found'824 }825 };826 requestMock.onFirstCall().resolves(fakeCommentsResponse, fakeCommentsResponse.body);827 requestMock.onSecondCall().resolves(fakeResponse);828 return scm829 .addPrComment({830 comment,831 prNum,832 scmUri,833 token,834 scmContext835 })836 .then(data => {837 assert.isNull(data);838 })839 .catch(error => {840 assert.calledWith(requestMock, expectedOptions);841 assert.match(error.message, '404 Reason "Resource not found" Caller "_addPrComment"');842 assert.match(error.status, 404);843 });844 });845 });846 describe('getFile', () => {847 const apiUrl = 'projects/repoId/repository/files/path%2Fto%2Ffile.txt';848 let expectedOptions;849 let fakeResponse;850 let params;851 beforeEach(() => {852 expectedOptions = {853 url: `${prefixUrl}/${apiUrl}`,854 method: 'GET',855 context: {856 token857 },858 searchParams: {859 ref: 'branchName'860 }861 };862 fakeResponse = {863 statusCode: 200,864 body: {865 encoding: 'ascii',866 content: 'dataValue'867 }868 };869 params = {870 scmUri,871 scmContext,872 token,873 path: 'path/to/file.txt'874 };875 requestMock.resolves(fakeResponse);876 });877 it('resolves to correct commit sha', () =>878 scm.getFile(params).then(content => {879 assert.calledWith(requestMock, expectedOptions);880 assert.deepEqual(content, 'dataValue');881 }));882 it('resolves to correct commit sha when rootDir is passed in', () => {883 params.scmUri = 'hostName:repoId:branchName:path/to/source';884 expectedOptions.url = `${prefixUrl}/projects/repoId/repository/files/path%2Fto%2Fsource%2Fpath%2Fto%2Ffile.txt`;885 return scm.getFile(params).then(content => {886 assert.calledWith(requestMock, expectedOptions);887 assert.deepEqual(content, 'dataValue');888 });889 });890 it('rejects if status code is not 200', () => {891 const err = new Error('404 Reason "Resource not found" Caller "_getFile"');892 err.status = 404;893 requestMock.rejects(err);894 return scm895 .getFile(params)896 .then(() => {897 assert.fail('Should not get here');898 })899 .catch(error => {900 assert.calledWith(requestMock, expectedOptions);901 assert.match(error.message, '404 Reason "Resource not found" Caller "_getFile"');902 assert.match(error.status, 404);903 });904 });905 it('rejects if fails', () => {906 const err = new Error('Gitlab API error');907 requestMock.rejects(err);908 return scm909 .getFile(params)910 .then(() => {911 assert.fail('Should not get here');912 })913 .catch(error => {914 assert.calledWith(requestMock, expectedOptions);915 assert.equal(error, err);916 });917 });918 });919 describe('getPermissions', () => {920 const apiUrl = 'projects/repoId';921 let expectedOptions;922 let fakeResponse;923 beforeEach(() => {924 expectedOptions = {925 url: `${prefixUrl}/${apiUrl}`,926 method: 'GET',927 context: {928 token929 }930 };931 fakeResponse = {932 statusCode: 200,933 body: {934 permissions: {935 project_access: {936 access_level: 50937 }938 }939 }940 };941 requestMock.resolves(fakeResponse);942 });943 it('get correct permissions for level 50', () =>944 scm945 .getPermissions({946 scmUri,947 token,948 scmContext949 })950 .then(permissions => {951 assert.calledOnce(requestMock);952 assert.calledWith(requestMock, expectedOptions);953 assert.deepEqual(permissions, {954 admin: true,955 push: true,956 pull: true957 });958 }));959 it('get correct permissions for level 40', () => {960 fakeResponse = {961 statusCode: 200,962 body: {963 permissions: {964 project_access: {965 access_level: 40966 }967 }968 }969 };970 requestMock.resolves(fakeResponse);971 return scm972 .getPermissions({973 scmUri,974 token,975 scmContext976 })977 .then(permissions => {978 assert.calledOnce(requestMock);979 assert.calledWith(requestMock, expectedOptions);980 assert.deepEqual(permissions, {981 admin: true,982 push: true,983 pull: true984 });985 });986 });987 it('get correct permissions for level 30', () => {988 fakeResponse = {989 statusCode: 200,990 body: {991 permissions: {992 project_access: {993 access_level: 30994 }995 }996 }997 };998 requestMock.resolves(fakeResponse);999 return scm1000 .getPermissions({1001 scmUri,1002 token,1003 scmContext1004 })1005 .then(permissions => {1006 assert.calledOnce(requestMock);1007 assert.calledWith(requestMock, expectedOptions);1008 assert.deepEqual(permissions, {1009 admin: false,1010 push: true,1011 pull: true1012 });1013 });1014 });1015 it('get correct permissions for level 20', () => {1016 fakeResponse = {1017 statusCode: 200,1018 body: {1019 permissions: {1020 project_access: {1021 access_level: 201022 }1023 }1024 }1025 };1026 requestMock.resolves(fakeResponse);1027 return scm1028 .getPermissions({1029 scmUri,1030 token,1031 scmContext1032 })1033 .then(permissions => {1034 assert.calledOnce(requestMock);1035 assert.calledWith(requestMock, expectedOptions);1036 assert.deepEqual(permissions, {1037 admin: false,1038 push: false,1039 pull: true1040 });1041 });1042 });1043 it('get correct permissions for level 10', () => {1044 fakeResponse = {1045 statusCode: 200,1046 body: {1047 permissions: {1048 project_access: {1049 access_level: 101050 }1051 }1052 }1053 };1054 requestMock.resolves(fakeResponse);1055 return scm1056 .getPermissions({1057 scmUri,1058 token,1059 scmContext1060 })1061 .then(permissions => {1062 assert.calledOnce(requestMock);1063 assert.calledWith(requestMock, expectedOptions);1064 assert.deepEqual(permissions, {1065 admin: false,1066 push: false,1067 pull: false1068 });1069 });1070 });1071 it('get correct permissions for level 90', () => {1072 fakeResponse = {1073 statusCode: 200,1074 body: {1075 permissions: {1076 project_access: {1077 access_level: 901078 }1079 }1080 }1081 };1082 requestMock.resolves(fakeResponse);1083 return scm1084 .getPermissions({1085 scmUri,1086 token,1087 scmContext1088 })1089 .then(permissions => {1090 assert.calledOnce(requestMock);1091 assert.calledWith(requestMock, expectedOptions);1092 assert.deepEqual(permissions, {1093 admin: false,1094 push: false,1095 pull: false1096 });1097 });1098 });1099 it('get correct permissions when no access_level is present', () => {1100 fakeResponse = {1101 statusCode: 200,1102 body: {}1103 };1104 requestMock.resolves(fakeResponse);1105 return scm1106 .getPermissions({1107 scmUri,1108 token,1109 scmContext1110 })1111 .then(permissions => {1112 assert.calledOnce(requestMock);1113 assert.calledWith(requestMock, expectedOptions);1114 assert.deepEqual(permissions, {1115 admin: false,1116 push: false,1117 pull: false1118 });1119 });1120 });1121 it('rejects if status code is not 200', () => {1122 const err = new Error('404 Reason "Resource not found" Caller "_getPermissions"');1123 err.status = 404;1124 requestMock.rejects(err);1125 return scm1126 .getPermissions({1127 scmUri,1128 token,1129 scmContext1130 })1131 .then(() => {1132 assert.fail('Should not get here');1133 })1134 .catch(error => {1135 assert.match(error.message, '404 Reason "Resource not found" Caller "_getPermissions"');1136 assert.match(error.status, 404);1137 });1138 });1139 it('rejects if fails', () => {1140 const error = new Error('Gitlab API error');1141 requestMock.rejects(error);1142 return scm1143 .getPermissions({1144 scmUri,1145 token,1146 scmContext1147 })1148 .then(() => {1149 assert.fail('Should not get here');1150 })1151 .catch(err => {1152 assert.equal(error, err);1153 });1154 });1155 });1156 describe('updateCommitStatus', () => {1157 let config;1158 let apiUrl;1159 let fakeResponse;1160 let expectedOptions;1161 beforeEach(() => {1162 config = {1163 scmUri,1164 scmContext,1165 sha: '1111111111111111111111111111111111111111',1166 buildStatus: 'SUCCESS',1167 token,1168 url: 'http://valid.url',1169 jobName: 'main',1170 pipelineId: 6751171 };1172 apiUrl = `projects/repoId/statuses/${config.sha}`;1173 fakeResponse = {1174 statusCode: 2011175 };1176 expectedOptions = {1177 url: `${prefixUrl}/${apiUrl}`,1178 method: 'POST',1179 context: {1180 token1181 },1182 json: {1183 context: 'Screwdriver/675/main',1184 target_url: config.url,1185 state: 'success',1186 description: 'Everything looks good!'1187 }1188 };1189 requestMock.resolves(fakeResponse);1190 });1191 it('successfully update status', () =>1192 scm.updateCommitStatus(config).then(() => {1193 assert.calledWith(requestMock, expectedOptions);1194 }));1195 it('successfully update status with correct values', () => {1196 config.buildStatus = 'FAILURE';1197 expectedOptions.json.context = 'Screwdriver/675/main';1198 expectedOptions.json.state = 'failed';1199 expectedOptions.json.description = 'Did not work as expected.';1200 return scm.updateCommitStatus(config).then(() => {1201 assert.calledWith(requestMock, expectedOptions);1202 });1203 });1204 it('rejects if status code is not 201 or 200', () => {1205 const err = new Error('401 Reason "Access token expired" Caller "_updateCommitStatus"');1206 err.status = 401;1207 requestMock.rejects(err);1208 return scm1209 .updateCommitStatus(config)1210 .then(() => {1211 assert.fail('Should not get here');1212 })1213 .catch(error => {1214 assert.calledWith(requestMock, expectedOptions);1215 assert.match(error.message, '401 Reason "Access token expired" Caller "_updateCommitStatus"');1216 assert.match(error.status, 401);1217 });1218 });1219 it('rejects if fails', () => {1220 const err = new Error('Gitlab API error');1221 requestMock.rejects(err);1222 return scm1223 .updateCommitStatus(config)1224 .then(() => {1225 assert.fail('Should not get here');1226 })1227 .catch(error => {1228 assert.calledWith(requestMock, expectedOptions);1229 assert.equal(error, err);1230 });1231 });1232 });1233 describe('getBellConfiguration', () => {1234 it('resolves a default configuration', () =>1235 scm.getBellConfiguration().then(config => {1236 assert.deepEqual(config, {1237 'gitlab:gitlab.com': {1238 clientId: 'myclientid',1239 clientSecret: 'myclientsecret',1240 config: {1241 uri: 'https://gitlab.com'1242 },1243 forceHttps: false,1244 isSecure: false,1245 provider: 'gitlab',1246 cookie: 'gitlab-gitlab.com'1247 }1248 });1249 }));1250 it('resolves a configuration for gitlabHost chenged from default', () => {1251 scm = new GitlabScm({1252 oauthClientId: 'abcdef',1253 oauthClientSecret: 'hijklm',1254 gitlabHost: 'mygitlab.com'1255 });1256 const expected = {1257 'gitlab:mygitlab.com': {1258 clientId: 'abcdef',1259 clientSecret: 'hijklm',1260 config: {1261 uri: 'https://mygitlab.com'1262 },1263 forceHttps: false,1264 isSecure: false,1265 provider: 'gitlab',1266 cookie: 'gitlab-mygitlab.com'1267 }1268 };1269 return scm.getBellConfiguration().then(config => {1270 assert.deepEqual(config, expected);1271 });1272 });1273 });1274 describe('getChangedFiles', () => {1275 const apiUrl = 'projects/28476/merge_requests/1/changes';1276 let type;1277 let expectedOptions;1278 let fakeResponse;1279 beforeEach(() => {1280 fakeResponse = {1281 statusCode: 200,1282 body: testChangedFiles1283 };1284 expectedOptions = {1285 url: `${prefixUrl}/${apiUrl}`,1286 method: 'GET',1287 context: {1288 token1289 }1290 };1291 requestMock.resolves(fakeResponse);1292 });1293 it('returns changed files for a push event payload', () => {1294 type = 'repo';1295 return scm1296 .getChangedFiles({1297 type,1298 token,1299 webhookConfig: testWebhookConfigPush1300 })1301 .then(result => {1302 assert.deepEqual(result, ['CHANGELOG', 'app/controller/application.rb']);1303 });1304 });1305 it('returns changed files for any given pr', () =>1306 scm1307 .getChangedFiles({1308 type: 'pr',1309 token,1310 webhookConfig: null,1311 scmUri: 'github.com:28476:master',1312 prNum: 11313 })1314 .then(result => {1315 assert.calledWith(requestMock, expectedOptions);1316 assert.deepEqual(result, ['test/screwdriver.yaml', 'README.md', 'screwdriver.yaml']);1317 }));1318 it('returns empty array for an event payload that is not type repo or pr', () => {1319 type = 'ping';1320 return scm1321 .getChangedFiles({1322 type,1323 token,1324 webhookConfig: testWebhookConfigOpen1325 })1326 .then(result => {1327 assert.deepEqual(result, []);1328 });1329 });1330 it('returns empty array for an event payload which does not have changed files', () => {1331 type = 'repo';1332 return scm1333 .getChangedFiles({1334 type,1335 token,1336 webhookConfig: testWebhookConfigPushBadHead1337 })1338 .then(result => {1339 assert.deepEqual(result, []);1340 });1341 });1342 });1343 describe('getPrInfo', () => {1344 const apiUrl = 'projects/repoId/merge_requests/1';1345 const config = {1346 scmUri,1347 token,1348 prNum: 11349 };1350 const sha = '8888888888888888888888888888888888888888';1351 let expectedOptions;1352 let fakeResponse;1353 beforeEach(() => {1354 fakeResponse = {1355 statusCode: 200,1356 body: testMergeRequest1357 };1358 expectedOptions = {1359 url: `${prefixUrl}/${apiUrl}`,1360 method: 'GET',1361 context: {1362 token1363 }1364 };1365 requestMock.resolves(fakeResponse);1366 });1367 it('returns a pull request with the given prNum', () =>1368 scm._getPrInfo(config).then(data => {1369 assert.calledWith(requestMock, expectedOptions);1370 assert.deepEqual(data, {1371 name: 'PR-1',1372 ref: 'pull/1/merge',1373 sha,1374 url: 'http://gitlab.example.com/my-group/my-project/merge_requests/1',1375 username: 'admin',1376 title: 'test1',1377 createTime: '2017-04-29T08:46:00Z',1378 userProfile: 'https://gitlab.example.com/admin',1379 prBranchName: 'test1',1380 baseBranch: 'test1',1381 mergeable: false,1382 prSource: 'fork'1383 });1384 }));1385 it('rejects when failing to lookup the SCM URI information', () => {1386 const testError = new Error('testError');1387 requestMock.rejects(testError);1388 return scm._getPrInfo(config).then(assert.fail, err => {1389 assert.instanceOf(err, Error);1390 assert.strictEqual(testError.message, err.message);1391 });1392 });1393 });1394 describe('getCheckoutCommand', () => {1395 let config;1396 beforeEach(() => {1397 config = {1398 branch: 'branchName',1399 host: 'hostName',1400 org: 'orgName',1401 repo: 'repoName',1402 sha: 'shaValue',1403 scmContext1404 };1405 });1406 it('resolves checkout command without prRef', () =>1407 scm.getCheckoutCommand(config).then(command => {1408 assert.deepEqual(command, testCommands);1409 }));1410 it('resolves checkout command with prRef', () => {1411 config.prRef = 'prBranch';1412 return scm.getCheckoutCommand(config).then(command => {1413 assert.deepEqual(command, testPrCommands);1414 });1415 });1416 it('resolves checkout command with custom username and email', () => {1417 config.prRef = 'prBranch';1418 scm = new GitlabScm({1419 oauthClientId: 'myclientid',1420 oauthClientSecret: 'myclientsecret',1421 username: 'abcd',1422 email: 'dev-null@my.email.com'1423 });1424 return scm.getCheckoutCommand(config).then(command => {1425 assert.deepEqual(command, testCustomPrCommands);1426 });1427 });1428 it('resolves checkout command with rootDir', () => {1429 config.rootDir = 'path/to/source';1430 scm = new GitlabScm({1431 oauthClientId: 'myclientid',1432 oauthClientSecret: 'myclientsecret',1433 username: 'abcd',1434 email: 'dev-null@my.email.com'1435 });1436 return scm.getCheckoutCommand(config).then(command => {1437 assert.deepEqual(command, testRootDirCommands);1438 });1439 });1440 it('promises to get the checkout command for a child pipeline', () => {1441 config.parentConfig = {1442 branch: 'master',1443 host: 'github.com',1444 org: 'screwdriver-cd',1445 repo: 'parent-to-guide',1446 sha: '54321'1447 };1448 return scm.getCheckoutCommand(config).then(command => {1449 assert.deepEqual(command, testChildCommands);1450 });1451 });1452 });1453 describe('stats', () => {1454 it('returns the correct stats', () => {1455 assert.deepEqual(scm.stats(), {1456 'gitlab:gitlab.com': {1457 requests: {1458 total: 0,1459 timeouts: 0,1460 success: 0,1461 failure: 0,1462 concurrent: 0,1463 averageTime: 01464 },1465 breaker: {1466 isClosed: true1467 }1468 }1469 });1470 });1471 });1472 describe('_addWebhook', () => {1473 let findWebhookResponse;1474 let expectedOptionsFind;1475 let expectedOptionsCreate;1476 const hookId = 'hookId';1477 const apiUrl = 'projects/repoId/hooks';1478 beforeEach(() => {1479 requestMock.onSecondCall().resolves({1480 statusCode: 2001481 });1482 expectedOptionsFind = {1483 url: `${prefixUrl}/${apiUrl}`,1484 method: 'GET',1485 context: {1486 token1487 }1488 };1489 expectedOptionsCreate = {1490 url: `${prefixUrl}/${apiUrl}`,1491 method: 'POST',1492 context: {1493 token1494 },1495 json: {1496 url: 'url',1497 push_events: true,1498 merge_requests_events: true1499 }1500 };1501 });1502 it('works', () => {1503 findWebhookResponse = {1504 body: [],1505 statusCode: 2001506 };1507 requestMock.onFirstCall().resolves(findWebhookResponse);1508 /* eslint-disable no-underscore-dangle */1509 return scm1510 ._addWebhook({1511 /* eslint-enable no-underscore-dangle */1512 scmUri,1513 token,1514 webhookUrl: 'url',1515 actions: ['merge_requests_events', 'push_events']1516 })1517 .then(() => {1518 assert.calledWith(requestMock.firstCall, expectedOptionsFind);1519 assert.calledWith(requestMock.secondCall, expectedOptionsCreate);1520 });1521 });1522 it('updates a pre-existing webhook', () => {1523 findWebhookResponse = {1524 statusCode: 200,1525 body: [1526 {1527 id: hookId,1528 url: 'url',1529 created_at: '2017-03-02T06:38:01.338Z',1530 push_events: true,1531 tag_push_events: false,1532 enable_ssl_verification: true,1533 project_id: 3,1534 issues_events: false,1535 merge_requests_events: true,1536 note_events: false,1537 build_events: false,1538 pipeline_events: false,1539 wiki_page_events: false1540 }1541 ]1542 };1543 expectedOptionsCreate.method = 'PUT';1544 expectedOptionsCreate.url = `${prefixUrl}/${apiUrl}/${hookId}`;1545 requestMock.onFirstCall().resolves(findWebhookResponse);1546 /* eslint-disable no-underscore-dangle */1547 return scm1548 ._addWebhook({1549 /* eslint-enable no-underscore-dangle */1550 scmUri,1551 token,1552 webhookUrl: 'url',1553 actions: ['merge_requests_events', 'push_events']1554 })1555 .then(() => {1556 assert.calledWith(requestMock.firstCall, expectedOptionsFind);1557 assert.calledWith(requestMock.secondCall, expectedOptionsCreate);1558 });1559 });1560 it('rejects when failing to get the current list of webhooks', () => {1561 const err = new Error(1562 '403 Reason "Your credentials lack one or more required privilege scopes." Caller "_findWebhook"'1563 );1564 err.status = 403;1565 requestMock.onFirstCall().rejects(err);1566 /* eslint-disable no-underscore-dangle */1567 return scm1568 ._addWebhook({1569 /* eslint-enable no-underscore-dangle */1570 scmUri,1571 token,1572 webhookUrl: 'url',1573 actions: ['merge_requests_events', 'push_events']1574 })1575 .then(assert.fail, error => {1576 assert.match(1577 error.message,1578 '403 Reason "Your credentials lack one or more ' +1579 'required privilege scopes." ' +1580 'Caller "_findWebhook"'1581 );1582 assert.match(error.status, 403);1583 });1584 });1585 it('rejects when failing to create a webhook', () => {1586 findWebhookResponse = {1587 statusCode: 200,1588 body: []1589 };1590 const err = new Error(1591 '403 Reason "Your credentials lack one or more required privilege scopes." Caller "_createWebhook"'1592 );1593 err.status = 403;1594 requestMock.onFirstCall().resolves(findWebhookResponse);1595 requestMock.onSecondCall().rejects(err);1596 /* eslint-disable no-underscore-dangle */1597 return scm1598 ._addWebhook({1599 /* eslint-enable no-underscore-dangle */1600 scmUri,1601 token,1602 url: 'url',1603 actions: ['merge_requests_events', 'push_events']1604 })1605 .then(assert.fail, error => {1606 assert.match(1607 error.message,1608 '403 Reason "Your credentials lack one or more ' +1609 'required privilege scopes." ' +1610 'Caller "_createWebhook"'1611 );1612 assert.match(error.status, 403);1613 });1614 });1615 it('rejects when failing to update a webhook', () => {1616 findWebhookResponse = {1617 statusCode: 200,1618 body: [1619 {1620 id: hookId,1621 url: 'url',1622 created_at: '2017-03-02T06:38:01.338Z',1623 push_events: true,1624 tag_push_events: false,1625 enable_ssl_verification: true,1626 project_id: 3,1627 issues_events: false,1628 merge_requests_events: true,1629 note_events: false,1630 build_events: false,1631 pipeline_events: false,1632 wiki_page_events: false1633 }1634 ]1635 };1636 const err = new Error(1637 '403 Reason "Your credentials lack one or more required privilege scopes." Caller "_createWebhook"'1638 );1639 err.status = 403;1640 requestMock.onFirstCall().resolves(findWebhookResponse);1641 requestMock.onSecondCall().rejects(err);1642 /* eslint-disable no-underscore-dangle */1643 return scm1644 ._addWebhook({1645 /* eslint-enable no-underscore-dangle */1646 scmUri,1647 token,1648 url: 'url',1649 actions: ['merge_requests_events', 'push_events']1650 })1651 .then(assert.fail, error => {1652 assert.strictEqual(1653 error.message,1654 '403 Reason "Your credentials lack one or more ' +1655 'required privilege scopes." ' +1656 'Caller "_createWebhook"'1657 );1658 assert.match(error.status, 403);1659 });1660 });1661 });1662 describe('_getOpenedPRs', () => {1663 const apiUrl = 'projects/repoId/merge_requests';1664 const expectedOptions = {1665 url: `${prefixUrl}/${apiUrl}`,1666 method: 'GET',1667 context: {1668 token1669 },1670 searchParams: {1671 state: 'opened'1672 }1673 };1674 it('returns response of expected format from Gitlab', () => {1675 requestMock.resolves({1676 statusCode: 200,1677 body: [1678 {1679 id: 1,1680 iid: 2,1681 target_branch: 'master',1682 source_branch: 'test1',1683 project_id: 3,1684 title: 'test 1',1685 created_at: '2011-01-26T19:01:12Z',1686 author: { username: 'collab1', web_url: '/collab1' },1687 web_url: '/merge_requests/1'1688 },1689 {1690 id: 2,1691 iid: 3,1692 target_branch: 'master',1693 source_branch: 'test2',1694 project_id: 3,1695 title: 'test 2',1696 created_at: '2011-01-26T19:01:12Z',1697 author: { username: 'collab2', web_url: '/collab2' },1698 web_url: '/merge_requests/2'1699 }1700 ]1701 });1702 // eslint-disable-next-line no-underscore-dangle1703 return scm1704 ._getOpenedPRs({1705 scmUri,1706 token1707 })1708 .then(response => {1709 assert.calledWith(requestMock, expectedOptions);1710 assert.deepEqual(response, [1711 {1712 name: 'PR-2',1713 ref: 'merge_requests/2',1714 username: 'collab1',1715 title: 'test 1',1716 createTime: '2011-01-26T19:01:12Z',1717 url: '/merge_requests/1',1718 userProfile: '/collab1'1719 },1720 {1721 name: 'PR-3',1722 ref: 'merge_requests/3',1723 username: 'collab2',1724 title: 'test 2',1725 createTime: '2011-01-26T19:01:12Z',1726 url: '/merge_requests/2',1727 userProfile: '/collab2'1728 }1729 ]);1730 });1731 });1732 });1733 describe('getScmContexts', () => {1734 it('returns a default scmContext', () => {1735 const result = scm.getScmContexts();1736 return assert.deepEqual(result, ['gitlab:gitlab.com']);1737 });1738 it('returns a scmContext for user setting gitlabHost', () => {1739 scm = new GitlabScm({1740 oauthClientId: 'abcdef',1741 oauthClientSecret: 'hijklm',1742 gitlabHost: 'mygitlab.com'1743 });1744 const result = scm.getScmContexts();1745 return assert.deepEqual(result, ['gitlab:mygitlab.com']);1746 });1747 });1748 describe('canHandleWebhook', () => {1749 it('returns a true for opened PR', () => {1750 const headers = {1751 'content-type': 'application/json',1752 'x-gitlab-event': 'Merge Request Hook'1753 };1754 return scm.canHandleWebhook(headers, testPayloadOpen).then(result => {1755 assert.strictEqual(result, true);1756 });1757 });1758 it('returns a true for closed PR', () => {1759 const headers = {1760 'content-type': 'application/json',1761 'x-gitlab-event': 'Merge Request Hook'1762 };1763 return scm.canHandleWebhook(headers, testPayloadClose).then(result => {1764 assert.strictEqual(result, true);1765 });1766 });1767 it('returns a true for push to repo event', () => {1768 const headers = {1769 'content-type': 'application/json',1770 'x-gitlab-event': 'Push Hook'1771 };1772 return scm.canHandleWebhook(headers, testPayloadPush).then(result => {1773 assert.strictEqual(result, true);1774 });1775 });1776 it('returns a false for scm not supporting', () => {1777 const headers = {1778 'x-hub-signature': 'sha1=a72eab99ad7f36f582f224df8d735091b06f1802',1779 'x-github-event': 'pull_request',1780 'x-github-delivery': '3c77bf80-9a2f-11e6-80d6-72f7fe03ea29'1781 };1782 return scm.canHandleWebhook(headers, testPayloadOpen).then(result => {1783 assert.strictEqual(result, false);1784 });1785 });1786 it('returns a false when parseHook resolves null', () => {1787 const headers = {1788 'content-type': 'application/json',1789 'x-gitlab-event': 'Push Hook'1790 };1791 scm._parseHook = sinon.stub();1792 scm._parseHook.resolves(null);1793 return scm.canHandleWebhook(headers, testPayloadOpen).then(result => {1794 assert.strictEqual(result, false);1795 });1796 });1797 it('returns a false when parseHook catches some error', () => {1798 const headers = {1799 'content-type': 'application/json',1800 'x-gitlab-event': 'Push Hook'1801 };1802 scm._parseHook = sinon.stub().rejects();1803 return scm.canHandleWebhook(headers, testPayloadOpen).then(result => {1804 assert.strictEqual(result, false);1805 });1806 });1807 });1808 describe('openPr', () => {1809 it('resolves null', () => {1810 scm.openPr({1811 checkoutUrl: 'https://hostName/username/repoName/tree/branchName',1812 token,1813 files: [1814 {1815 name: 'file.txt',1816 content: 'content'1817 }1818 ],1819 title: 'update file',1820 message: 'update file'1821 }).then(result => assert.isNull(result));1822 });1823 });...
image-size.test.js
Source:image-size.test.js
1const should = require('should');2const sinon = require('sinon');3const nock = require('nock');4const path = require('path');5const errors = require('@tryghost/errors');6const fs = require('fs');7const ImageSize = require('../../../../../core/server/lib/image/image-size');8describe('lib/image: image size', function () {9 // use a 1x1 gif in nock responses because it's really small and easy to work with10 const GIF1x1 = Buffer.from('R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==', 'base64');11 afterEach(function () {12 sinon.restore();13 nock.cleanAll();14 });15 it('[success] should have an image size function', function () {16 const imageSize = new ImageSize({config: {17 get: () => {}18 }, tpl: {}, storage: {}, storageUtils: {}, validator: {}, urlUtils: {}, request: {}});19 should.exist(imageSize.getImageSizeFromUrl);20 should.exist(imageSize.getImageSizeFromStoragePath);21 });22 describe('getImageSizeFromUrl', function () {23 it('[success] should return image dimensions from probe request for probe-supported extension', function (done) {24 const url = 'http://img.stockfresh.com/files/f/feedough/x/11/1540353_20925115.jpg';25 const expectedImageObject = {26 height: 1,27 url: 'http://img.stockfresh.com/files/f/feedough/x/11/1540353_20925115.jpg',28 width: 129 };30 const requestMock = nock('http://img.stockfresh.com')31 .get('/files/f/feedough/x/11/1540353_20925115.jpg')32 .reply(200, GIF1x1);33 const imageSize = new ImageSize({config: {34 get: () => {}35 }, tpl: {}, storage: {}, storageUtils: {36 isLocalImage: () => false37 }, validator: {38 isURL: () => true39 }, urlUtils: {}, request: {}});40 imageSize.getImageSizeFromUrl(url).then(function (res) {41 requestMock.isDone().should.be.true();42 should.exist(res);43 res.width.should.be.equal(expectedImageObject.width);44 res.height.should.be.equal(expectedImageObject.height);45 res.url.should.be.equal(expectedImageObject.url);46 done();47 }).catch(done);48 });49 it('[success] should return image dimensions from fetch request for non-probe-supported extension', function (done) {50 const url = 'https://static.wixstatic.com/media/355241_d31358572a2542c5a44738ddcb59e7ea.cur';51 const expectedImageObject = {52 height: 1,53 url: 'https://static.wixstatic.com/media/355241_d31358572a2542c5a44738ddcb59e7ea.cur',54 width: 155 };56 const requestMock = nock('https://static.wixstatic.com').get('/random-path').reply(404);57 const imageSize = new ImageSize({config: {58 get: () => {}59 }, tpl: {}, storage: {}, storageUtils: {60 isLocalImage: () => false61 }, validator: {62 isURL: () => true63 }, urlUtils: {}, request: (requestUrl) => {64 if (requestUrl === url) {65 return Promise.resolve({66 body: GIF1x167 });68 }69 return Promise.reject();70 }});71 imageSize.getImageSizeFromUrl(url).then(function (res) {72 requestMock.isDone().should.be.false();73 should.exist(res);74 res.width.should.be.equal(expectedImageObject.width);75 res.height.should.be.equal(expectedImageObject.height);76 res.url.should.be.equal(expectedImageObject.url);77 done();78 }).catch(done);79 });80 it('[success] should return image dimensions when no image extension given', function (done) {81 const url = 'https://www.zomato.com/logo/18163505/minilogo';82 const expectedImageObject = {83 height: 1,84 url: 'https://www.zomato.com/logo/18163505/minilogo',85 width: 186 };87 const requestMock = nock('https://www.zomato.com')88 .get('/logo/18163505/minilogo')89 .reply(200, GIF1x1);90 const imageSize = new ImageSize({config: {91 get: () => {}92 }, tpl: {}, storage: {}, storageUtils: {93 isLocalImage: () => false94 }, validator: {95 isURL: () => true96 }, urlUtils: {}, request: {}});97 imageSize.getImageSizeFromUrl(url).then(function (res) {98 requestMock.isDone().should.be.true();99 should.exist(res);100 res.width.should.be.equal(expectedImageObject.width);101 res.height.should.be.equal(expectedImageObject.height);102 res.url.should.be.equal(expectedImageObject.url);103 done();104 }).catch(done);105 });106 it('[success] should returns largest image value for .ico files', function (done) {107 const url = 'https://super-website.com/media/icon.ico';108 const expectedImageObject = {109 height: 64,110 url: 'https://super-website.com/media/icon.ico',111 width: 64112 };113 const requestMock = nock('https://super-website.com').get('/random-path').reply(404);114 const imageSize = new ImageSize({config: {115 get: () => {}116 }, tpl: {}, storage: {}, storageUtils: {117 isLocalImage: () => false118 }, validator: {119 isURL: () => true120 }, urlUtils: {}, request: (requestUrl) => {121 if (requestUrl === url) {122 return Promise.resolve({123 body: fs.readFileSync(path.join(__dirname, '../../../../utils/fixtures/images/favicon_multi_sizes.ico'))124 });125 }126 return Promise.reject();127 }});128 imageSize.getImageSizeFromUrl(url).then(function (res) {129 requestMock.isDone().should.be.false();130 should.exist(res);131 res.width.should.be.equal(expectedImageObject.width);132 res.height.should.be.equal(expectedImageObject.height);133 res.url.should.be.equal(expectedImageObject.url);134 done();135 }).catch(done);136 });137 it('[success] should return image dimensions asset path images', function (done) {138 const url = '/assets/img/logo.png?v=d30c3d1e41';139 const expectedImageObject = {140 height: 1,141 url: 'http://myblog.com/assets/img/logo.png?v=d30c3d1e41',142 width: 1143 };144 const urlForStub = sinon.stub().withArgs('home').returns('http://myblog.com/');145 const urlGetSubdirStub = sinon.stub().returns('');146 const requestMock = nock('http://myblog.com')147 .get('/assets/img/logo.png?v=d30c3d1e41')148 .reply(200, GIF1x1);149 const imageSize = new ImageSize({config: {150 get: () => {}151 }, tpl: {}, storage: {}, storageUtils: {152 isLocalImage: () => false153 }, validator: {154 isURL: () => true155 }, urlUtils: {156 urlFor: urlForStub,157 getSubdir: urlGetSubdirStub,158 urlJoin: function () {159 if ([...arguments].join('') === 'http://myblog.com///assets/img/logo.png?v=d30c3d1e41') {160 return expectedImageObject.url;161 }162 return '';163 }164 }, request: (requestUrl) => {165 if (requestUrl === url) {166 return Promise.resolve({167 body: GIF1x1168 });169 }170 return Promise.reject();171 }});172 imageSize.getImageSizeFromUrl(url).then(function (res) {173 requestMock.isDone().should.be.true();174 should.exist(res);175 res.width.should.be.equal(expectedImageObject.width);176 res.height.should.be.equal(expectedImageObject.height);177 res.url.should.be.equal(expectedImageObject.url);178 done();179 }).catch(done);180 });181 it('[success] should return image dimensions for gravatar images request', function (done) {182 const url = '//www.gravatar.com/avatar/ef6dcde5c99bb8f685dd451ccc3e050a?s=250&d=mm&r=x';183 const expectedImageObject = {184 height: 1,185 url: '//www.gravatar.com/avatar/ef6dcde5c99bb8f685dd451ccc3e050a?s=250&d=mm&r=x',186 width: 1187 };188 const requestMock = nock('http://www.gravatar.com')189 .get('/avatar/ef6dcde5c99bb8f685dd451ccc3e050a?s=250&d=mm&r=x')190 .reply(200, GIF1x1);191 const imageSize = new ImageSize({config: {192 get: () => {}193 }, tpl: {}, storage: {}, storageUtils: {194 isLocalImage: () => false195 }, validator: {196 isURL: () => true197 }, urlUtils: {}, request: {}});198 imageSize.getImageSizeFromUrl(url).then(function (res) {199 requestMock.isDone().should.be.true();200 should.exist(res);201 res.width.should.be.equal(expectedImageObject.width);202 res.height.should.be.equal(expectedImageObject.height);203 res.url.should.be.equal(expectedImageObject.url);204 done();205 }).catch(done);206 });207 it('[success] can handle redirect (probe-image-size)', function (done) {208 const url = 'http://noimagehere.com/files/f/feedough/x/11/1540353_20925115.jpg';209 const expectedImageObject = {210 height: 1,211 url: 'http://noimagehere.com/files/f/feedough/x/11/1540353_20925115.jpg',212 width: 1213 };214 const requestMock = nock('http://noimagehere.com')215 .get('/files/f/feedough/x/11/1540353_20925115.jpg')216 .reply(301, null, {217 location: 'http://someredirectedurl.com/files/f/feedough/x/11/1540353_20925115.jpg'218 });219 const secondRequestMock = nock('http://someredirectedurl.com')220 .get('/files/f/feedough/x/11/1540353_20925115.jpg')221 .reply(200, GIF1x1);222 const imageSize = new ImageSize({config: {223 get: () => {}224 }, tpl: {}, storage: {}, storageUtils: {225 isLocalImage: () => false226 }, validator: {227 isURL: () => true228 }, urlUtils: {}, request: {}});229 imageSize.getImageSizeFromUrl(url).then(function (res) {230 requestMock.isDone().should.be.true();231 secondRequestMock.isDone().should.be.true();232 should.exist(res);233 res.width.should.be.equal(expectedImageObject.width);234 res.height.should.be.equal(expectedImageObject.height);235 res.url.should.be.equal(expectedImageObject.url);236 done();237 }).catch(done);238 });239 it('[success] should switch to local file storage if available', function (done) {240 const url = '/content/images/favicon.png';241 const expectedImageObject = {242 height: 100,243 url: 'http://myblog.com/content/images/favicon.png',244 width: 100245 };246 const storagePath = path.join(__dirname, '../../../../utils/fixtures/images/');247 const urlForStub = sinon.stub();248 urlForStub.withArgs('image').returns('http://myblog.com/content/images/favicon.png');249 urlForStub.withArgs('home').returns('http://myblog.com/');250 const urlGetSubdirStub = sinon.stub();251 urlGetSubdirStub.returns('');252 const requestMock = nock('http://myblog.com')253 .get('/content/images/favicon.png')254 .reply(200, {255 body: '<Buffer 2c be a4 40 f7 87 73 1e 57 2c c1 e4 0d 79 03 95 42 f0 42 2e 41 95 27 c9 5c 35 a7 71 2c 09 5a 57 d3 04 1e 83 03 28 07 96 b0 c8 88 65 07 7a d1 d6 63 50>'256 });257 const imageSize = new ImageSize({config: {258 get: () => {}259 }, tpl: {}, storage: {260 getStorage: () => ({261 read: obj => fs.promises.readFile(obj.path)262 })263 }, storageUtils: {264 isLocalImage: () => true,265 getLocalFileStoragePath: imageUrl => path.join(storagePath, imageUrl.replace(/.*\//, ''))266 }, validator: {}, urlUtils: {267 urlFor: urlForStub,268 getSubdir: urlGetSubdirStub269 }, request: {}});270 imageSize.getImageSizeFromUrl(url).then(function (res) {271 requestMock.isDone().should.be.false();272 should.exist(res);273 should.exist(res.width);274 res.width.should.be.equal(expectedImageObject.width);275 should.exist(res.height);276 res.height.should.be.equal(expectedImageObject.height);277 should.exist(res.url);278 res.url.should.be.equal(expectedImageObject.url);279 done();280 }).catch(done);281 });282 it('[failure] can handle an error with statuscode not 200 (probe-image-size)', function (done) {283 const url = 'http://noimagehere.com/files/f/feedough/x/11/1540353_20925115.jpg';284 const requestMock = nock('http://noimagehere.com')285 .get('/files/f/feedough/x/11/1540353_20925115.jpg')286 .reply(404);287 const imageSize = new ImageSize({config: {288 get: () => {}289 }, tpl: {}, storage: {}, storageUtils: {290 isLocalImage: () => false291 }, validator: {292 isURL: () => true293 }, urlUtils: {}, request: {}});294 imageSize.getImageSizeFromUrl(url)295 .catch(function (err) {296 requestMock.isDone().should.be.true();297 should.exist(err);298 err.errorType.should.be.equal('NotFoundError');299 err.message.should.be.equal('Image not found.');300 done();301 }).catch(done);302 });303 it('[failure] can handle an error with statuscode not 200 (image-size)', function (done) {304 const url = 'http://noimagehere.com/files/f/feedough/x/11/1540353_20925115.cur';305 const requestMock = nock('http://noimagehere.com')306 .get('/files/f/feedough/x/11/1540353_20925115.cur')307 .reply(404);308 class NotFound extends Error {309 constructor(message) {310 super(message);311 this.code = 'ENOENT';312 this.statusCode = 404;313 }314 }315 const imageSize = new ImageSize({config: {316 get: () => {}317 }, tpl: {}, storage: {}, storageUtils: {318 isLocalImage: () => false319 }, validator: {320 isURL: () => true321 }, urlUtils: {}, request: (requestUrl) => {322 if (requestUrl === url) {323 return Promise.reject(new NotFound());324 }325 return Promise.reject();326 }});327 imageSize.getImageSizeFromUrl(url)328 .catch(function (err) {329 requestMock.isDone().should.be.false();330 should.exist(err);331 err.errorType.should.be.equal('NotFoundError');332 err.message.should.be.equal('Image not found.');333 done();334 }).catch(done);335 });336 it('[failure] handles invalid URL', function (done) {337 const url = 'Not-a-valid-url';338 const imageSize = new ImageSize({config: {339 get: () => {}340 }, tpl: {}, storage: {}, storageUtils: {341 isLocalImage: () => false342 }, validator: {343 isURL: () => false344 }, urlUtils: {}, request: {}});345 imageSize.getImageSizeFromUrl(url)346 .catch(function (err) {347 should.exist(err);348 err.errorType.should.be.equal('InternalServerError');349 err.message.should.be.equal('URL empty or invalid.');350 done();351 }).catch(done);352 });353 it('[failure] will timeout', function (done) {354 const url = 'https://static.wixstatic.com/media/355241_d31358572a2542c5a44738ddcb59e7ea.jpg_256';355 const requestMock = nock('https://static.wixstatic.com')356 .get('/media/355241_d31358572a2542c5a44738ddcb59e7ea.jpg_256')357 .delayConnection(10)358 .reply(408);359 const imageSize = new ImageSize({config: {360 get: (key) => {361 if (key === 'times:getImageSizeTimeoutInMS') {362 return 1;363 }364 }365 }, tpl: {}, storage: {}, storageUtils: {366 isLocalImage: () => false367 }, validator: {368 isURL: () => true369 }, urlUtils: {}, request: {}});370 imageSize.getImageSizeFromUrl(url)371 .catch(function (err) {372 requestMock.isDone().should.be.true();373 should.exist(err);374 err.errorType.should.be.equal('InternalServerError');375 err.message.should.be.equal('Request timed out.');376 done();377 }).catch(done);378 });379 it('[failure] returns error if \`probe-image-size`\ module throws error', function (done) {380 const url = 'https://static.wixstatic.com/media/355241_d31358572a2542c5a44738ddcb59e7ea.jpg';381 const requestMock = nock('https://static.wixstatic.com')382 .get('/media/355241_d31358572a2542c5a44738ddcb59e7ea.jpg')383 .reply(200, Buffer.from('FFD8 FFC0 0004 00112233 FFD9'.replace(/ /g, ''), 'hex'));384 const imageSize = new ImageSize({config: {385 get: () => {}386 }, tpl: {}, storage: {}, storageUtils: {387 isLocalImage: () => false388 }, validator: {389 isURL: () => true390 }, urlUtils: {}, request: {}});391 imageSize.getImageSizeFromUrl(url)392 .then(() => {393 true.should.be.false('succeeded when expecting failure');394 })395 .catch(function (err) {396 requestMock.isDone().should.be.true();397 should.exist(err);398 err.errorType.should.be.equal('InternalServerError');399 done();400 }).catch(done);401 });402 it('[failure] returns error if \`image-size`\ module throws error', function (done) {403 const url = 'https://static.wixstatic.com/media/355241_d31358572a2542c5a44738ddcb59e7ea.cur';404 const requestMock = nock('https://static.wixstatic.com')405 .get('/media/nope.cur')406 .reply(404);407 const imageSize = new ImageSize({config: {408 get: () => {}409 }, tpl: {}, storage: {}, storageUtils: {410 isLocalImage: () => false411 }, validator: {412 isURL: () => true413 }, urlUtils: {}, request: (requestUrl) => {414 if (requestUrl === url) {415 return Promise.resolve({416 body: Buffer.from('2c be a4 40 f7 87 73 1e 57 2c c1 e4 0d 79 03 95 42 f0 42 2e 41 95 27 c9 5c 35 a7 71 2c 09 5a 57 d3 04 1e 83 03 28 07 96 b0 c8 88 65 07 7a d1 d6 63 50'.replace(/ /g, ''), 'hex')417 });418 }419 return Promise.reject();420 }});421 imageSize.getImageSizeFromUrl(url)422 .then(() => {423 true.should.be.false('succeeded when expecting failure');424 })425 .catch(function (err) {426 requestMock.isDone().should.be.false();427 should.exist(err);428 err.errorType.should.be.equal('InternalServerError');429 done();430 }).catch(done);431 });432 it('[failure] returns error if request errors', function (done) {433 const url = 'https://notarealwebsite.com/images/notapicture.dds';434 const imageSize = new ImageSize({config: {435 get: () => {}436 }, tpl: {}, storage: {}, storageUtils: {437 isLocalImage: () => false438 }, validator: {439 isURL: () => true440 }, urlUtils: {}, request: () => {441 return Promise.reject({});442 }});443 imageSize.getImageSizeFromUrl(url)444 .catch(function (err) {445 should.exist(err);446 err.errorType.should.be.equal('InternalServerError');447 err.message.should.be.equal('Unknown Request error.');448 done();449 }).catch(done);450 });451 });452 describe('getImageSizeFromStoragePath', function () {453 it('[success] should return image dimensions for locally stored images', function (done) {454 const url = '/content/images/ghost-logo.png';455 const expectedImageObject = {456 height: 257,457 url: 'http://myblog.com/content/images/ghost-logo.png',458 width: 800459 };460 const storagePath = path.join(__dirname, '../../../../utils/fixtures/images/');461 const urlForStub = sinon.stub();462 urlForStub.withArgs('image').returns('http://myblog.com/content/images/ghost-logo.png');463 urlForStub.withArgs('home').returns('http://myblog.com/');464 const urlGetSubdirStub = sinon.stub();465 urlGetSubdirStub.returns('');466 const imageSize = new ImageSize({config: {467 get: () => {}468 }, tpl: {}, storage: {469 getStorage: () => ({470 read: obj => fs.promises.readFile(obj.path)471 })472 }, storageUtils: {473 isLocalImage: () => true,474 getLocalFileStoragePath: imageUrl => path.join(storagePath, imageUrl.replace(/.*\//, ''))475 }, validator: {}, urlUtils: {476 urlFor: urlForStub,477 getSubdir: urlGetSubdirStub478 }, request: () => {479 return Promise.reject({});480 }});481 imageSize.getImageSizeFromStoragePath(url).then(function (res) {482 should.exist(res);483 should.exist(res.width);484 res.width.should.be.equal(expectedImageObject.width);485 should.exist(res.height);486 res.height.should.be.equal(expectedImageObject.height);487 should.exist(res.url);488 res.url.should.be.equal(expectedImageObject.url);489 done();490 }).catch(done);491 });492 it('[success] should return image dimensions for locally stored images with subdirectory', function (done) {493 const url = '/content/images/favicon_too_large.png';494 const expectedImageObject = {495 height: 1010,496 url: 'http://myblog.com/blog/content/images/favicon_too_large.png',497 width: 1010498 };499 const storagePath = path.join(__dirname, '../../../../utils/fixtures/images/');500 const urlForStub = sinon.stub();501 urlForStub.withArgs('image').returns('http://myblog.com/blog/content/images/favicon_too_large.png');502 urlForStub.withArgs('home').returns('http://myblog.com/');503 const urlGetSubdirStub = sinon.stub();504 urlGetSubdirStub.returns('/blog');505 const imageSize = new ImageSize({config: {506 get: () => {}507 }, tpl: {}, storage: {508 getStorage: () => ({509 read: obj => fs.promises.readFile(obj.path)510 })511 }, storageUtils: {512 isLocalImage: () => true,513 getLocalFileStoragePath: imageUrl => path.join(storagePath, imageUrl.replace(/.*\//, ''))514 }, validator: {}, urlUtils: {515 urlFor: urlForStub,516 getSubdir: urlGetSubdirStub517 }, request: () => {518 return Promise.reject({});519 }});520 imageSize.getImageSizeFromStoragePath(url).then(function (res) {521 should.exist(res);522 should.exist(res.width);523 res.width.should.be.equal(expectedImageObject.width);524 should.exist(res.height);525 res.height.should.be.equal(expectedImageObject.height);526 should.exist(res.url);527 res.url.should.be.equal(expectedImageObject.url);528 done();529 }).catch(done);530 });531 it('[success] should return largest image dimensions for locally stored .ico image', function (done) {532 const url = 'http://myblog.com/content/images/favicon_multi_sizes.ico';533 const expectedImageObject = {534 height: 64,535 url: 'http://myblog.com/content/images/favicon_multi_sizes.ico',536 width: 64537 };538 const storagePath = path.join(__dirname, '../../../../utils/fixtures/images/');539 const urlForStub = sinon.stub();540 urlForStub.withArgs('image').returns('http://myblog.com/content/images/favicon_multi_sizes.ico');541 urlForStub.withArgs('home').returns('http://myblog.com/');542 const urlGetSubdirStub = sinon.stub();543 urlGetSubdirStub.returns('');544 const imageSize = new ImageSize({config: {545 get: () => {}546 }, tpl: {}, storage: {547 getStorage: () => ({548 read: obj => fs.promises.readFile(obj.path)549 })550 }, storageUtils: {551 isLocalImage: () => true,552 getLocalFileStoragePath: imageUrl => path.join(storagePath, imageUrl.replace(/.*\//, ''))553 }, validator: {}, urlUtils: {554 urlFor: urlForStub,555 getSubdir: urlGetSubdirStub556 }, request: () => {557 return Promise.reject({});558 }});559 imageSize.getImageSizeFromStoragePath(url).then(function (res) {560 should.exist(res);561 should.exist(res.width);562 res.width.should.be.equal(expectedImageObject.width);563 should.exist(res.height);564 res.height.should.be.equal(expectedImageObject.height);565 should.exist(res.url);566 res.url.should.be.equal(expectedImageObject.url);567 done();568 }).catch(done);569 });570 it('[success] should return image dimensions for locally stored .webp image', function (done) {571 const url = 'http://myblog.com/content/images/ghosticon.webp';572 const expectedImageObject = {573 height: 249,574 url: 'http://myblog.com/content/images/ghosticon.webp',575 width: 249576 };577 const storagePath = path.join(__dirname, '../../../../utils/fixtures/images/');578 const urlForStub = sinon.stub();579 urlForStub.withArgs('image').returns('http://myblog.com/content/images/ghosticon.webp');580 urlForStub.withArgs('home').returns('http://myblog.com/');581 const urlGetSubdirStub = sinon.stub();582 urlGetSubdirStub.returns('');583 const imageSize = new ImageSize({config: {584 get: () => {}585 }, tpl: {}, storage: {586 getStorage: () => ({587 read: obj => fs.promises.readFile(obj.path)588 })589 }, storageUtils: {590 isLocalImage: () => true,591 getLocalFileStoragePath: imageUrl => path.join(storagePath, imageUrl.replace(/.*\//, ''))592 }, validator: {}, urlUtils: {593 urlFor: urlForStub,594 getSubdir: urlGetSubdirStub595 }, request: () => {596 return Promise.reject({});597 }});598 imageSize.getImageSizeFromStoragePath(url).then(function (res) {599 should.exist(res);600 should.exist(res.width);601 res.width.should.be.equal(expectedImageObject.width);602 should.exist(res.height);603 res.height.should.be.equal(expectedImageObject.height);604 should.exist(res.url);605 res.url.should.be.equal(expectedImageObject.url);606 done();607 }).catch(done);608 });609 it('[failure] returns error if storage adapter errors', function (done) {610 const url = '/content/images/not-existing-image.png';611 const storagePath = path.join(__dirname, '../../../../utils/fixtures/images/');612 const urlForStub = sinon.stub();613 urlForStub.withArgs('image').returns('http://myblog.com/content/images/not-existing-image.png');614 urlForStub.withArgs('home').returns('http://myblog.com/');615 const urlGetSubdirStub = sinon.stub();616 urlGetSubdirStub.returns('');617 const imageSize = new ImageSize({config: {618 get: () => {}619 }, tpl: {}, storage: {620 getStorage: () => ({621 read: () => {622 return Promise.reject(new errors.NotFoundError());623 }624 })625 }, storageUtils: {626 isLocalImage: () => true,627 getLocalFileStoragePath: imageUrl => path.join(storagePath, imageUrl.replace(/.*\//, ''))628 }, validator: {}, urlUtils: {629 urlFor: urlForStub,630 getSubdir: urlGetSubdirStub631 }, request: () => {632 return Promise.reject({});633 }});634 imageSize.getImageSizeFromStoragePath(url)635 .catch(function (err) {636 should.exist(err);637 (err instanceof errors.NotFoundError).should.eql(true);638 done();639 }).catch(done);640 });641 it('[failure] returns error if \`image-size`\ module throws error', function (done) {642 const url = '/content/images/malformed.svg';643 const urlForStub = sinon.stub();644 urlForStub.withArgs('image').returns('http://myblog.com/content/images/malformed.svg');645 urlForStub.withArgs('home').returns('http://myblog.com/');646 const urlGetSubdirStub = sinon.stub();647 urlGetSubdirStub.returns('');648 const imageSize = new ImageSize({config: {649 get: () => {}650 }, tpl: {}, storage: {651 getStorage: () => ({652 read: () => {653 return Promise.resolve(Buffer.from('<svg xmlns="http://www.w3.org/2000/svg viewBox="0 0 100 100>/svg>'));654 }655 })656 }, storageUtils: {657 isLocalImage: () => true,658 getLocalFileStoragePath: () => ''659 }, validator: {}, urlUtils: {660 urlFor: urlForStub,661 getSubdir: urlGetSubdirStub662 }, request: () => {663 return Promise.reject({});664 }});665 imageSize.getImageSizeFromStoragePath(url)666 .catch(function (err) {667 should.exist(err);668 done();669 }).catch(done);670 });671 });...
external-request_spec.js
Source:external-request_spec.js
1const sinon = require('sinon');2const should = require('should');3const rewire = require('rewire');4const nock = require('nock');5const externalRequest = rewire('../../../core/server/lib/request-external');6const configUtils = require('../../utils/configUtils');7// for sinon stubs8const dnsPromises = require('dns').promises;9describe('External Request', function () {10 describe('with private ip', function () {11 beforeEach(function () {12 sinon.stub(dnsPromises, 'lookup').callsFake(function () {13 return Promise.resolve({address: '192.168.0.1'});14 });15 });16 afterEach(function () {17 configUtils.restore();18 sinon.restore();19 nock.cleanAll();20 });21 it('allows configured hostname', function () {22 configUtils.set('url', 'http://example.com');23 const url = 'http://example.com/endpoint/';24 const expectedResponse = {25 body: 'Response body',26 url: 'http://example.com/endpoint/',27 statusCode: 20028 };29 const options = {30 headers: {31 'User-Agent': 'Mozilla/5.0'32 }33 };34 const requestMock = nock('http://example.com')35 .get('/endpoint/')36 .reply(200, 'Response body');37 return externalRequest(url, options).then(function (res) {38 requestMock.isDone().should.be.true();39 should.exist(res);40 should.exist(res.body);41 res.body.should.be.equal(expectedResponse.body);42 should.exist(res.url);43 res.statusCode.should.be.equal(expectedResponse.statusCode);44 should.exist(res.statusCode);45 res.url.should.be.equal(expectedResponse.url);46 });47 });48 it('allows configured hostname+port', function () {49 configUtils.set('url', 'http://example.com:2368');50 const url = 'http://example.com:2368/endpoint/';51 const expectedResponse = {52 body: 'Response body',53 url: 'http://example.com:2368/endpoint/',54 statusCode: 20055 };56 const options = {57 headers: {58 'User-Agent': 'Mozilla/5.0'59 }60 };61 const requestMock = nock('http://example.com:2368')62 .get('/endpoint/')63 .reply(200, 'Response body');64 return externalRequest(url, options).then(function (res) {65 requestMock.isDone().should.be.true();66 should.exist(res);67 should.exist(res.body);68 res.body.should.be.equal(expectedResponse.body);69 should.exist(res.url);70 res.statusCode.should.be.equal(expectedResponse.statusCode);71 should.exist(res.statusCode);72 res.url.should.be.equal(expectedResponse.url);73 });74 });75 it('blocks configured hostname with incorrect port', function () {76 configUtils.set('url', 'http://example.com');77 const url = 'http://example.com:1234/endpoint/';78 const options = {79 headers: {80 'User-Agent': 'Mozilla/5.0'81 }82 };83 return externalRequest(url, options).then(() => {84 throw new Error('Request should have rejected with non-permitted IP message');85 }, (err) => {86 should.exist(err);87 err.message.should.be.equal('URL resolves to a non-permitted private IP block');88 });89 });90 it('blocks configured hostname+port with incorrect port', function () {91 configUtils.set('url', 'http://example.com:2368');92 const url = 'http://example.com:1234/endpoint/';93 const options = {94 headers: {95 'User-Agent': 'Mozilla/5.0'96 }97 };98 return externalRequest(url, options).then(() => {99 throw new Error('Request should have rejected with non-permitted IP message');100 }, (err) => {101 should.exist(err);102 err.message.should.be.equal('URL resolves to a non-permitted private IP block');103 });104 });105 it('blocks on request', function () {106 const url = 'http://some-website.com/';107 const options = {108 headers: {109 'User-Agent': 'Mozilla/5.0'110 }111 };112 const requestMock = nock('http://some-website.com')113 .get('/files/')114 .reply(200, 'Response');115 return externalRequest(url, options).then(function () {116 throw new Error('Request should have rejected with non-permitted IP message');117 }, (err) => {118 should.exist(err);119 err.message.should.be.equal('URL resolves to a non-permitted private IP block');120 requestMock.isDone().should.be.false();121 });122 });123 it('blocks on redirect', function () {124 configUtils.set('url', 'http://some-website.com');125 const url = 'http://some-website.com/endpoint/';126 const options = {127 headers: {128 'User-Agent': 'Mozilla/5.0'129 }130 };131 const requestMock = nock('http://some-website.com')132 .get('/endpoint/')133 .reply(301, 'Oops, got redirected',134 {135 location: 'http://someredirectedurl.com/files/'136 });137 const secondRequestMock = nock('http://someredirectedurl.com')138 .get('/files/')139 .reply(200, 'Redirected response');140 return externalRequest(url, options).then(function () {141 throw new Error('Request should have rejected with non-permitted IP message');142 }, (err) => {143 should.exist(err);144 err.message.should.be.equal('URL resolves to a non-permitted private IP block');145 requestMock.isDone().should.be.true();146 secondRequestMock.isDone().should.be.false();147 });148 });149 });150 describe('general behaviour', function () {151 beforeEach(function () {152 sinon.stub(dnsPromises, 'lookup').callsFake(function (host) {153 return Promise.resolve({address: '123.123.123.123'});154 });155 });156 afterEach(function () {157 configUtils.restore();158 sinon.restore();159 nock.cleanAll();160 });161 it('[success] should return response for http request', function () {162 const url = 'http://some-website.com/endpoint/';163 const expectedResponse = {164 body: 'Response body',165 url: 'http://some-website.com/endpoint/',166 statusCode: 200167 };168 const options = {169 headers: {170 'User-Agent': 'Mozilla/5.0'171 }172 };173 const requestMock = nock('http://some-website.com')174 .get('/endpoint/')175 .reply(200, 'Response body');176 return externalRequest(url, options).then(function (res) {177 requestMock.isDone().should.be.true();178 should.exist(res);179 should.exist(res.body);180 res.body.should.be.equal(expectedResponse.body);181 should.exist(res.url);182 res.statusCode.should.be.equal(expectedResponse.statusCode);183 should.exist(res.statusCode);184 res.url.should.be.equal(expectedResponse.url);185 });186 });187 it('[success] can handle redirect', function () {188 const url = 'http://some-website.com/endpoint/';189 const expectedResponse = {190 body: 'Redirected response',191 url: 'http://someredirectedurl.com/files/',192 statusCode: 200193 };194 const options = {195 headers: {196 'User-Agent': 'Mozilla/5.0'197 }198 };199 const requestMock = nock('http://some-website.com')200 .get('/endpoint/')201 .reply(301, 'Oops, got redirected',202 {203 location: 'http://someredirectedurl.com/files/'204 });205 const secondRequestMock = nock('http://someredirectedurl.com')206 .get('/files/')207 .reply(200, 'Redirected response');208 return externalRequest(url, options).then(function (res) {209 requestMock.isDone().should.be.true();210 secondRequestMock.isDone().should.be.true();211 should.exist(res);212 should.exist(res.body);213 res.body.should.be.equal(expectedResponse.body);214 should.exist(res.url);215 res.statusCode.should.be.equal(expectedResponse.statusCode);216 should.exist(res.statusCode);217 res.url.should.be.equal(expectedResponse.url);218 });219 });220 it('[failure] can handle invalid url', function () {221 const url = 'test';222 const options = {223 headers: {224 'User-Agent': 'Mozilla/5.0'225 }226 };227 return externalRequest(url, options).then(() => {228 throw new Error('Request should have rejected with invalid url message');229 }, (err) => {230 should.exist(err);231 err.message.should.be.equal('URL empty or invalid.');232 });233 });234 it('[failure] can handle empty url', function () {235 const url = '';236 const options = {237 headers: {238 'User-Agent': 'Mozilla/5.0'239 }240 };241 return externalRequest(url, options).then(() => {242 throw new Error('Request should have rejected with invalid url message');243 }, (err) => {244 should.exist(err);245 err.message.should.be.equal('URL empty or invalid.');246 });247 });248 it('[failure] can handle an error with statuscode not 200', function () {249 const url = 'http://nofilehere.com/files/test.txt';250 const options = {251 headers: {252 'User-Agent': 'Mozilla/5.0'253 }254 };255 const requestMock = nock('http://nofilehere.com')256 .get('/files/test.txt')257 .reply(404);258 return externalRequest(url, options).then(() => {259 throw new Error('Request should have errored');260 }, (err) => {261 requestMock.isDone().should.be.true();262 should.exist(err);263 err.statusMessage.should.be.equal('Not Found');264 });265 });266 it('[failure] returns error if request errors', function () {267 const url = 'http://nofilehere.com/files/test.txt';268 const options = {269 headers: {270 'User-Agent': 'Mozilla/5.0'271 }272 };273 const requestMock = nock('http://nofilehere.com')274 .get('/files/test.txt')275 .times(3) // 1 original request + 2 default retries276 .reply(500, {message: 'something awful happened', code: 'AWFUL_ERROR'});277 return externalRequest(url, options).then(() => {278 throw new Error('Request should have errored with an awful error');279 }, (err) => {280 requestMock.isDone().should.be.true();281 should.exist(err);282 err.statusMessage.should.be.equal('Internal Server Error');283 err.body.should.match(/something awful happened/);284 err.body.should.match(/AWFUL_ERROR/);285 });286 });287 });...
request-external.test.js
Source:request-external.test.js
1const sinon = require('sinon');2const should = require('should');3const rewire = require('rewire');4const nock = require('nock');5const externalRequest = rewire('../../../../core/server/lib/request-external');6const configUtils = require('../../../utils/configUtils');7// for sinon stubs8const dnsPromises = require('dns').promises;9describe('External Request', function () {10 describe('with private ip', function () {11 beforeEach(function () {12 sinon.stub(dnsPromises, 'lookup').callsFake(function () {13 return Promise.resolve({address: '192.168.0.1'});14 });15 });16 afterEach(function () {17 configUtils.restore();18 sinon.restore();19 nock.cleanAll();20 });21 it('allows configured hostname', function () {22 configUtils.set('url', 'http://example.com');23 const url = 'http://example.com/endpoint/';24 const expectedResponse = {25 body: 'Response body',26 url: 'http://example.com/endpoint/',27 statusCode: 20028 };29 const options = {30 headers: {31 'User-Agent': 'Mozilla/5.0'32 }33 };34 const requestMock = nock('http://example.com')35 .get('/endpoint/')36 .reply(200, 'Response body');37 return externalRequest(url, options).then(function (res) {38 requestMock.isDone().should.be.true();39 should.exist(res);40 should.exist(res.body);41 res.body.should.be.equal(expectedResponse.body);42 should.exist(res.url);43 res.statusCode.should.be.equal(expectedResponse.statusCode);44 should.exist(res.statusCode);45 res.url.should.be.equal(expectedResponse.url);46 });47 });48 it('allows configured hostname+port', function () {49 configUtils.set('url', 'http://example.com:2368');50 const url = 'http://example.com:2368/endpoint/';51 const expectedResponse = {52 body: 'Response body',53 url: 'http://example.com:2368/endpoint/',54 statusCode: 20055 };56 const options = {57 headers: {58 'User-Agent': 'Mozilla/5.0'59 }60 };61 const requestMock = nock('http://example.com:2368')62 .get('/endpoint/')63 .reply(200, 'Response body');64 return externalRequest(url, options).then(function (res) {65 requestMock.isDone().should.be.true();66 should.exist(res);67 should.exist(res.body);68 res.body.should.be.equal(expectedResponse.body);69 should.exist(res.url);70 res.statusCode.should.be.equal(expectedResponse.statusCode);71 should.exist(res.statusCode);72 res.url.should.be.equal(expectedResponse.url);73 });74 });75 it('blocks configured hostname with incorrect port', function () {76 configUtils.set('url', 'http://example.com');77 const url = 'http://example.com:1234/endpoint/';78 const options = {79 headers: {80 'User-Agent': 'Mozilla/5.0'81 }82 };83 return externalRequest(url, options).then(() => {84 throw new Error('Request should have rejected with non-permitted IP message');85 }, (err) => {86 should.exist(err);87 err.message.should.be.equal('URL resolves to a non-permitted private IP block');88 });89 });90 it('blocks configured hostname+port with incorrect port', function () {91 configUtils.set('url', 'http://example.com:2368');92 const url = 'http://example.com:1234/endpoint/';93 const options = {94 headers: {95 'User-Agent': 'Mozilla/5.0'96 }97 };98 return externalRequest(url, options).then(() => {99 throw new Error('Request should have rejected with non-permitted IP message');100 }, (err) => {101 should.exist(err);102 err.message.should.be.equal('URL resolves to a non-permitted private IP block');103 });104 });105 it('blocks on request', function () {106 const url = 'http://some-website.com/';107 const options = {108 headers: {109 'User-Agent': 'Mozilla/5.0'110 }111 };112 const requestMock = nock('http://some-website.com')113 .get('/files/')114 .reply(200, 'Response');115 return externalRequest(url, options).then(function () {116 throw new Error('Request should have rejected with non-permitted IP message');117 }, (err) => {118 should.exist(err);119 err.message.should.be.equal('URL resolves to a non-permitted private IP block');120 requestMock.isDone().should.be.false();121 });122 });123 it('blocks on redirect', function () {124 configUtils.set('url', 'http://some-website.com');125 const url = 'http://some-website.com/endpoint/';126 const options = {127 headers: {128 'User-Agent': 'Mozilla/5.0'129 }130 };131 const requestMock = nock('http://some-website.com')132 .get('/endpoint/')133 .reply(301, 'Oops, got redirected',134 {135 location: 'http://someredirectedurl.com/files/'136 });137 const secondRequestMock = nock('http://someredirectedurl.com')138 .get('/files/')139 .reply(200, 'Redirected response');140 return externalRequest(url, options).then(function () {141 throw new Error('Request should have rejected with non-permitted IP message');142 }, (err) => {143 should.exist(err);144 err.message.should.be.equal('URL resolves to a non-permitted private IP block');145 requestMock.isDone().should.be.true();146 secondRequestMock.isDone().should.be.false();147 });148 });149 });150 describe('general behaviour', function () {151 beforeEach(function () {152 sinon.stub(dnsPromises, 'lookup').callsFake(function (host) {153 return Promise.resolve({address: '123.123.123.123'});154 });155 });156 afterEach(function () {157 configUtils.restore();158 sinon.restore();159 nock.cleanAll();160 });161 it('[success] should return response for http request', function () {162 const url = 'http://some-website.com/endpoint/';163 const expectedResponse = {164 body: 'Response body',165 url: 'http://some-website.com/endpoint/',166 statusCode: 200167 };168 const options = {169 headers: {170 'User-Agent': 'Mozilla/5.0'171 }172 };173 const requestMock = nock('http://some-website.com')174 .get('/endpoint/')175 .reply(200, 'Response body');176 return externalRequest(url, options).then(function (res) {177 requestMock.isDone().should.be.true();178 should.exist(res);179 should.exist(res.body);180 res.body.should.be.equal(expectedResponse.body);181 should.exist(res.url);182 res.statusCode.should.be.equal(expectedResponse.statusCode);183 should.exist(res.statusCode);184 res.url.should.be.equal(expectedResponse.url);185 });186 });187 it('[success] can handle redirect', function () {188 const url = 'http://some-website.com/endpoint/';189 const expectedResponse = {190 body: 'Redirected response',191 url: 'http://someredirectedurl.com/files/',192 statusCode: 200193 };194 const options = {195 headers: {196 'User-Agent': 'Mozilla/5.0'197 }198 };199 const requestMock = nock('http://some-website.com')200 .get('/endpoint/')201 .reply(301, 'Oops, got redirected',202 {203 location: 'http://someredirectedurl.com/files/'204 });205 const secondRequestMock = nock('http://someredirectedurl.com')206 .get('/files/')207 .reply(200, 'Redirected response');208 return externalRequest(url, options).then(function (res) {209 requestMock.isDone().should.be.true();210 secondRequestMock.isDone().should.be.true();211 should.exist(res);212 should.exist(res.body);213 res.body.should.be.equal(expectedResponse.body);214 should.exist(res.url);215 res.statusCode.should.be.equal(expectedResponse.statusCode);216 should.exist(res.statusCode);217 res.url.should.be.equal(expectedResponse.url);218 });219 });220 it('[failure] can handle invalid url', function () {221 const url = 'test';222 const options = {223 headers: {224 'User-Agent': 'Mozilla/5.0'225 }226 };227 return externalRequest(url, options).then(() => {228 throw new Error('Request should have rejected with invalid url message');229 }, (err) => {230 should.exist(err);231 err.message.should.be.equal('URL empty or invalid.');232 });233 });234 it('[failure] can handle empty url', function () {235 const url = '';236 const options = {237 headers: {238 'User-Agent': 'Mozilla/5.0'239 }240 };241 return externalRequest(url, options).then(() => {242 throw new Error('Request should have rejected with invalid url message');243 }, (err) => {244 should.exist(err);245 err.message.should.be.equal('URL empty or invalid.');246 });247 });248 it('[failure] can handle an error with statuscode not 200', function () {249 const url = 'http://nofilehere.com/files/test.txt';250 const options = {251 headers: {252 'User-Agent': 'Mozilla/5.0'253 }254 };255 const requestMock = nock('http://nofilehere.com')256 .get('/files/test.txt')257 .reply(404);258 return externalRequest(url, options).then(() => {259 throw new Error('Request should have errored');260 }, (err) => {261 requestMock.isDone().should.be.true();262 should.exist(err);263 err.statusMessage.should.be.equal('Not Found');264 });265 });266 it('[failure] returns error if request errors', function () {267 const url = 'http://nofilehere.com/files/test.txt';268 const options = {269 headers: {270 'User-Agent': 'Mozilla/5.0'271 },272 retry: 0273 };274 const requestMock = nock('http://nofilehere.com')275 .get('/files/test.txt')276 .reply(500, {message: 'something awful happened', code: 'AWFUL_ERROR'});277 return externalRequest(url, options).then(() => {278 throw new Error('Request should have errored with an awful error');279 }, (err) => {280 requestMock.isDone().should.be.true();281 should.exist(err);282 err.statusMessage.should.be.equal('Internal Server Error');283 err.body.should.match(/something awful happened/);284 err.body.should.match(/AWFUL_ERROR/);285 });286 });287 });...
toBeRequestedWith.js
Source:toBeRequestedWith.js
...25 }26 }27 return false;28 }, isNot, { ...options, wait: isNot ? 0 : options.wait });29 const message = utils_1.enhanceError('mock', minifyRequestedWith(requestedWith), minifyRequestMock(actual, requestedWith) || 'was not called', this, verb, expectation, '', options);30 return {31 pass,32 message: () => message,33 };34 });35}36exports.toBeRequestedWithFn = toBeRequestedWithFn;37const methodMatcher = (method, expected) => {38 if (typeof expected === 'undefined') {39 return true;40 }41 if (!Array.isArray(expected)) {42 expected = [expected];43 }...
request_spec.js
Source:request_spec.js
1const should = require('should');2const rewire = require('rewire');3const nock = require('nock');4const request = rewire('../../../core/server/lib/request');5describe('Request', function () {6 it('[success] should return response for http request', function () {7 const url = 'http://some-website.com/endpoint/';8 const expectedResponse = {9 body: 'Response body',10 url: 'http://some-website.com/endpoint/',11 statusCode: 20012 };13 const options = {14 headers: {15 'User-Agent': 'Mozilla/5.0'16 }17 };18 const requestMock = nock('http://some-website.com')19 .get('/endpoint/')20 .reply(200, 'Response body');21 return request(url, options).then(function (res) {22 requestMock.isDone().should.be.true();23 should.exist(res);24 should.exist(res.body);25 res.body.should.be.equal(expectedResponse.body);26 should.exist(res.url);27 res.statusCode.should.be.equal(expectedResponse.statusCode);28 should.exist(res.statusCode);29 res.url.should.be.equal(expectedResponse.url);30 });31 });32 it('[success] can handle redirect', function () {33 const url = 'http://some-website.com/endpoint/';34 const expectedResponse = {35 body: 'Redirected response',36 url: 'http://someredirectedurl.com/files/',37 statusCode: 20038 };39 const options = {40 headers: {41 'User-Agent': 'Mozilla/5.0'42 }43 };44 const requestMock = nock('http://some-website.com')45 .get('/endpoint/')46 .reply(301, 'Oops, got redirected',47 {48 location: 'http://someredirectedurl.com/files/'49 });50 const secondRequestMock = nock('http://someredirectedurl.com')51 .get('/files/')52 .reply(200, 'Redirected response');53 return request(url, options).then(function (res) {54 requestMock.isDone().should.be.true();55 secondRequestMock.isDone().should.be.true();56 should.exist(res);57 should.exist(res.body);58 res.body.should.be.equal(expectedResponse.body);59 should.exist(res.url);60 res.statusCode.should.be.equal(expectedResponse.statusCode);61 should.exist(res.statusCode);62 res.url.should.be.equal(expectedResponse.url);63 });64 });65 it('[failure] can handle invalid url', function () {66 const url = 'test';67 const options = {68 headers: {69 'User-Agent': 'Mozilla/5.0'70 }71 };72 return request(url, options).then(() => {73 throw new Error('Request should have rejected with invalid url message');74 }, (err) => {75 should.exist(err);76 err.message.should.be.equal('URL empty or invalid.');77 });78 });79 it('[failure] can handle empty url', function () {80 const url = '';81 const options = {82 headers: {83 'User-Agent': 'Mozilla/5.0'84 }85 };86 return request(url, options).then(() => {87 throw new Error('Request should have rejected with invalid url message');88 }, (err) => {89 should.exist(err);90 err.message.should.be.equal('URL empty or invalid.');91 });92 });93 it('[failure] can handle an error with statuscode not 200', function () {94 const url = 'http://nofilehere.com/files/test.txt';95 const options = {96 headers: {97 'User-Agent': 'Mozilla/5.0'98 }99 };100 const requestMock = nock('http://nofilehere.com')101 .get('/files/test.txt')102 .reply(404);103 return request(url, options).then(() => {104 throw new Error('Request should have errored');105 }, (err) => {106 requestMock.isDone().should.be.true();107 should.exist(err);108 err.statusMessage.should.be.equal('Not Found');109 });110 });111 it('[failure] returns error if request errors', function () {112 const url = 'http://nofilehere.com/files/test.txt';113 const options = {114 headers: {115 'User-Agent': 'Mozilla/5.0'116 }117 };118 const requestMock = nock('http://nofilehere.com')119 .get('/files/test.txt')120 .times(3) // 1 original request + 2 default retries121 .reply(500, {message: 'something awful happened', code: 'AWFUL_ERROR'});122 return request(url, options).then(() => {123 throw new Error('Request should have errored with an awful error');124 }, (err) => {125 requestMock.isDone().should.be.true();126 should.exist(err);127 err.statusMessage.should.be.equal('Internal Server Error');128 err.body.should.match(/something awful happened/);129 err.body.should.match(/AWFUL_ERROR/);130 });131 });132 it('[failure] should timeout when taking too long', function () {133 const url = 'http://some-website.com/endpoint/';134 const options = {135 headers: {136 'User-Agent': 'Mozilla/5.0'137 },138 timeout: 1,139 retry: 0 // got retries by default so we're disabling this behavior140 };141 nock('http://some-website.com')142 .get('/endpoint/')143 .delay(20)144 .reply(200, 'Response body');145 return request(url, options).then(() => {146 throw new Error('Should have timed out');147 }, (err) => {148 err.code.should.be.equal('ETIMEDOUT');149 });150 });...
keyValuePairController.test.js
Source:keyValuePairController.test.js
...8const testObj1 = { key: keyPrefix + timeMills, value: valuePrefix + '1' };9const testObj2 = { key: keyPrefix + timeMills * 2, value: valuePrefix + '2' };10describe('KeyValuePairController Test', () => {11 it('Should return status 200 and the saved object', async () => {12 const reqMock = new RequestMock();13 const resMockReceived = new ResponseMock();14 const resMockSent = new ResponseMock();15 reqMock.body = testObj1;16 resMockReceived.status(200).send(testObj1);17 let throwedError1;18 try {19 await kvpController.save(reqMock, resMockSent);20 } catch (error) {21 throwedError1 = error;22 }23 assert.deepStrictEqual(resMockSent, resMockReceived);24 assert.strictEqual(throwedError1, undefined);25 });26 it('Should return status 500 and an error object', async () => {27 const reqMock = new RequestMock();28 const resMockReceived = new ResponseMock();29 const resMockSent = new ResponseMock();30 const errorMock = {31 message: `ERROR: key ${testObj1.key} already exists`,32 code: 50033 };34 reqMock.body = testObj1;35 resMockReceived.status(500).send(errorMock);36 let throwedError1;37 try {38 await kvpController.save(reqMock, resMockSent);39 } catch (error) {40 throwedError1 = error;41 }42 assert.deepStrictEqual(resMockSent, resMockReceived);43 assert.strictEqual(throwedError1, undefined);44 });45 it('Should return an array of objects including the previouslly saved object', async () => {46 const reqMock = new RequestMock();47 const resMockSent = new ResponseMock();48 await kvpController.findAll(reqMock, resMockSent);49 const obj1 = resMockSent.msg.find(kvp => kvp.key === testObj1.key);50 assert.strictEqual(Array.isArray(resMockSent.msg), true);51 assert.notStrictEqual(obj1, undefined);52 });53 it('Should return status 200 and the expected object', async () => {54 const reqMock = new RequestMock();55 const resMockReceived = new ResponseMock();56 const resMockSent = new ResponseMock();57 reqMock.params = {58 key: testObj1.key59 };60 resMockReceived.status(200).send(testObj1);61 let throwedError1;62 try {63 await kvpController.findById(reqMock, resMockSent);64 } catch (error) {65 throwedError1 = error;66 }67 assert.deepStrictEqual(resMockSent, resMockReceived);68 assert.strictEqual(throwedError1, undefined);69 });70 it('Should return status 404 and an error object', async () => {71 const reqMock = new RequestMock();72 const resMockReceived = new ResponseMock();73 const resMockSent = new ResponseMock();74 const errorMock = {75 message: `KeyValuePair not found for id: ${testObj2.key}`,76 code: 40477 };78 reqMock.params = {79 key: testObj2.key80 };81 resMockReceived.status(404).send(errorMock);82 let throwedError1;83 try {84 await kvpController.findById(reqMock, resMockSent);85 } catch (error) {86 throwedError1 = error;87 }88 assert.deepStrictEqual(resMockSent, resMockReceived);89 assert.strictEqual(throwedError1, undefined);90 });91 it('Should return status 200', async () => {92 const reqMock = new RequestMock();93 const resMockReceived = new ResponseMock();94 const resMockSent = new ResponseMock();95 reqMock.params = {96 key: testObj1.key97 };98 resMockReceived.status(200).send();99 let throwedError1;100 try {101 await kvpController.deleteById(reqMock, resMockSent);102 } catch (error) {103 throwedError1 = error;104 }105 assert.deepStrictEqual(resMockSent, resMockReceived);106 assert.strictEqual(throwedError1, undefined);107 });108 it('Should return status 404 and an error object', async () => {109 const reqMock = new RequestMock();110 const resMockReceived = new ResponseMock();111 const resMockSent = new ResponseMock();112 const errorMock = {113 message: `KeyValuePair not found for id: ${testObj2.key}`,114 code: 404115 };116 reqMock.params = {117 key: testObj2.key118 };119 resMockReceived.status(404).send(errorMock);120 let throwedError1;121 try {122 await kvpController.deleteById(reqMock, resMockSent);123 } catch (error) {...
request-mock.js
Source:request-mock.js
1var expect = require('expect.js');2var RequestMock = function (options, method, url) {3 this.options = options;4 this.method = method;5 this.url = url;6 this._header = {};7};8RequestMock.prototype.send = function (body) {9 this._data = body;10 expect(body).to.eql(this.options.body);11 return this;12};13RequestMock.prototype.set = function (key, value) {14 expect(this.options.headers).to.have.key(key);15 expect(value).to.eql(this.options.headers[key]);16 this._header[key] = value;17 delete this.options.headers[key];18 return this;19};20RequestMock.prototype.abort = function () {21};22RequestMock.prototype.withCredentials = function () {23 return this;24};25RequestMock.prototype.end = function (cb) {26 expect(this.options.headers).to.eql({});27 this.options.cb(cb);28 return this;29};...
Using AI Code Generation
1import { RequestMock } from 'testcafe';2import { RequestLogger } from 'testcafe';3import { Selector } from 'testcafe';4import { ClientFunction } from 'testcafe';5import { Role } from 'testcafe';6import { t } from 'testcafe';7import { fixture } from 'testcafe';8import { page } from 'testcafe';9import { test } from 'testcafe';10import { RequestHook } from 'testcafe';11import { RequestLogger } from 'testcafe';12import { RequestMock } from 'testcafe';13import { Selector } from 'testcafe';14import { ClientFunction } from 'testcafe';15import { Role } from 'testcafe';16import { t } from 'testcafe';17import { fixture } from 'testcafe';18import { page } from 'testcafe';19import { test } from 'testcafe';20import { RequestHook } from 'testcafe';21import { RequestLogger } from 'testcafe';22import { RequestMock } from 'testcafe';23import { Selector } from 'testcafe';
Using AI Code Generation
1import { RequestMock } from 'testcafe';2const mock = RequestMock()3 .respond(null, 500, { 'access-control-allow-origin': '*' });4 .requestHooks(mock);5test('My test', async t => {6});7import { RequestLogger } from '
Using AI Code Generation
1import { RequestLogger, RequestMock } from 'testcafe';2const logger = RequestLogger(/.*/, {3});4const mock = RequestMock()5 .respond(null, 200, {6 })7 .respond('{"id": 1, "title": "delectus aut autem", "completed": false}', 200, {8 });9test('My first test', async t => {10 .setNativeDialogHandler(() => true)11 .click('#populate')12 .click('#submit-button');13});14 .requestHooks(logger, mock)('My second test', async t => {15 .expect(logger.contains(record => record.response.statusCode === 200)).ok();16});
Using AI Code Generation
1import { Selector } from 'testcafe';2test('My first test', async t => {3});4import { Selector } from 'testcafe';5test('My first test', async t => {6});7import { Selector } from 'testcafe';8test('My first test', async t => {9});
Using AI Code Generation
1import { RequestMock } from 'testcafe';2const mock = RequestMock()3 .respond('Hello, world!', 200, {4 });5 .requestHooks(mock);6test('My test', async t => {7});8import { RequestLogger } from 'testcafe';9 .requestHooks(logger);10test('My test', async t => {11});12import { RequestLogger } from 'testcafe';13 .requestHooks(logger);14test('My test', async t => {15});16import { RequestLogger } from 'testcafe';17 .requestHooks(logger);18test('My test', async t => {19});20import { RequestLogger } from 'testcafe';21 .requestHooks(logger);22test('My test', async t => {23});24import { RequestLogger } from 'testcafe';25 .requestHooks(logger);26test('My test', async t => {
Using AI Code Generation
1import { Selector } from 'testcafe';2import { RequestMock } from 'testcafe';3const mock = RequestMock()4 .respond([5 { "id": 1, "make": "Toyota", "model": "Camry", "year": 2018 },6 { "id": 2, "make": "Honda", "model": "Accord", "year": 2017 },7 { "id": 3, "make": "Ford", "model": "Fusion", "year": 2016 }8 ], 200, { 'access-control-allow-origin': '*' })9 .requestHooks(mock);10test('My first test', async t => {11 .click(Selector('button').withText('Get Cars'))12 .expect(Selector('div').withText('Toyota').exists).ok()13 .expect(Selector('div').withText('Honda').exists).ok()14 .expect(Selector('div').withText('Ford').exists).ok()15});16const express = require('express');17const app = express();18const port = 3000;19app.get('/api/cars', (req, res) => {20 res.setHeader('Access-Control-Allow-Origin', '*');21 res.json([22 { "id": 1, "make": "Toyota", "model": "Camry", "year": 2018 },23 { "id": 2, "make": "Honda", "model": "Accord", "year": 2017 },24 { "id": 3, "make": "Ford", "model": "Fusion", "year": 2016 }25 ]);26});27app.listen(port, () => console.log(`Example app listening on port ${port}!`));
Using AI Code Generation
1import { RequestMock } from 'testcafe';2const mock = RequestMock()3 .respond('{"data": "mocked"}', 200, {4 });5test('My test', async t => {6 .setNativeDialogHandler(() => true)7 .expect(true).ok();8});9 at Function.Module._resolveFilename (internal/modules/cjs/loader.js:1026:15)10 at Function.Module._load (internal/modules/cjs/loader.js:898:27)11 at Module.require (internal/modules/cjs/loader.js:1090:19)12 at require (internal/modules/cjs/helpers.js:75:18)13 at Object.<anonymous> (/Users/xxxxxx/Documents/xxxxxx/test.js:1:15)14 at Module._compile (internal/modules/cjs/loader.js:1158:30)15 at Object.Module._extensions..js (internal/modules/cjs/loader.js:1178:10)16 at Module.load (internal/modules/cjs/loader.js:1002:32)17 at Function.Module._load (internal/modules/cjs/loader.js:901:14)18 at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:74:12)19### How would you reproduce the current behavior (if this is a bug)?
Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!