diff --git a/.vscode/launch.json b/.vscode/launch.json index 136d2c78dc67711d73a72654abbc3913231591cd..277561dcd9f509de6c13d7f30b4dbec6fbb55a8c 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -32,7 +32,7 @@ "--disable-extensions", "--extensionDevelopmentPath=${workspaceRoot}", "--extensionTestsPath=${workspaceRoot}/out/test/integration/", - "" + "" ], "preLaunchTask": "${defaultBuildTask}", "stopOnEntry": false diff --git a/docs/writing-tests.md b/docs/writing-tests.md index 15d8d683f0488b77be4311d4ed1deaee33d9bfee..55dad1247823bef51fcd0655f91c38c8037e55ec 100644 --- a/docs/writing-tests.md +++ b/docs/writing-tests.md @@ -43,7 +43,7 @@ We use [`msw`](https://mswjs.io/docs/) to intercept any requests and return prep ### Debugging integration tests -For debugging of the integration tests, we first need to create a test workspace (the `npm run test-integration` task doesn't need this step because it does it automatically). We can do that by running ```npm run create-test-workspace``` script. Then we copy the output to `.vscode/launch.json` instead of the placeholder in the "Integration Tests" launch configuration arguments. +For debugging of the integration tests, we first need to create a test workspace (the `npm run test-integration` task doesn't need this step because it does it automatically). We can do that by running ```npm run create-test-workspace``` script. This script pastes the reference to generated workspace in `.vscode/launch.json`. Then we can debug the by running the "Integration Tests" [Launch configuration]. diff --git a/package-lock.json b/package-lock.json index 0de47897789d0b58dab06240daeb5f9a7bf5cb5c..7daf6d73c5668fd14541c85db698787742a77a8d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1377,6 +1377,18 @@ "@babel/types": "^7.3.0" } }, + "@types/bluebird": { + "version": "3.5.32", + "resolved": "https://registry.npmjs.org/@types/bluebird/-/bluebird-3.5.32.tgz", + "integrity": "sha512-dIOxFfI0C+jz89g6lQ+TqhGgPQ0MxSnh/E4xuC0blhFtyW269+mPG5QeLgbdwst/LvdP8o1y0o/Gz5EHXLec/g==", + "dev": true + }, + "@types/caseless": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.2.tgz", + "integrity": "sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w==", + "dev": true + }, "@types/color-name": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", @@ -1475,6 +1487,56 @@ "integrity": "sha512-boy4xPNEtiw6N3abRhBi/e7hNvy3Tt8E9ZRAQrwAGzoCGZS/1wjo9KY7JHhnfnEsG5wSjDbymCozUM9a3ea7OQ==", "dev": true }, + "@types/request": { + "version": "2.48.5", + "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.5.tgz", + "integrity": "sha512-/LO7xRVnL3DxJ1WkPGDQrp4VTV1reX9RkC85mJ+Qzykj2Bdw+mG15aAfDahc76HtknjzE16SX/Yddn6MxVbmGQ==", + "dev": true, + "requires": { + "@types/caseless": "*", + "@types/node": "*", + "@types/tough-cookie": "*", + "form-data": "^2.5.0" + }, + "dependencies": { + "form-data": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + } + } + }, + "@types/request-promise": { + "version": "4.1.46", + "resolved": "https://registry.npmjs.org/@types/request-promise/-/request-promise-4.1.46.tgz", + "integrity": "sha512-3Thpj2Va5m0ji3spaCk8YKrjkZyZc6RqUVOphA0n/Xet66AW/AiOAs5vfXhQIL5NmkaO7Jnun7Nl9NEjJ2zBaw==", + "dev": true, + "requires": { + "@types/bluebird": "*", + "@types/request": "*" + } + }, + "@types/sinon": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-9.0.4.tgz", + "integrity": "sha512-sJmb32asJZY6Z2u09bl0G2wglSxDlROlAejCjsnor+LzBMz17gu8IU7vKC/vWDnv9zEq2wqADHVXFjf4eE8Gdw==", + "dev": true, + "requires": { + "@types/sinonjs__fake-timers": "*" + } + }, + "@types/sinonjs__fake-timers": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.1.tgz", + "integrity": "sha512-yYezQwGWty8ziyYLdZjwxyMb0CZR49h8JALHGrxjQHWlqGgc8kLdHEgWrgL0uZ29DMvEVBDnHU2Wg36zKSIUtA==", + "dev": true + }, "@types/stack-utils": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", @@ -1490,6 +1552,12 @@ "@types/node": "*" } }, + "@types/tough-cookie": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.0.tgz", + "integrity": "sha512-I99sngh224D0M7XgW1s120zxCt3VYQ3IQsuw3P3jbq5GG4yc79+ZjyKznyOGIQrflfylLgcfekeZW/vk0yng6A==", + "dev": true + }, "@types/vscode": { "version": "1.46.0", "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.46.0.tgz", diff --git a/package.json b/package.json index 187d2f5ed46566357a461c8022deda66269f9308..76119625c87f9433bbbb13bca46e7ee1b52aa159 100644 --- a/package.json +++ b/package.json @@ -232,7 +232,7 @@ "minItems": 1, "items": { "type": "object", - "title": "Custom GitLab Quety", + "title": "Custom GitLab Query", "required": [ "name" ], @@ -347,22 +347,22 @@ "createdAfter": { "type": "string", "format": "date", - "description": "Return GitLab items created after the given date. It is not applicable for vulnerabilities" + "description": "Return GitLab items created after the given date. ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z. It is not applicable for vulnerabilities" }, "createdBefore": { "type": "string", "format": "date", - "description": "Return GitLab items created before the given date. It is not applicable for vulnerabilities" + "description": "Return GitLab items created before the given date. ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z. It is not applicable for vulnerabilities" }, "updatedAfter": { "type": "string", "format": "date", - "description": "Return GitLab items updated after the given date. It is not applicable for vulnerabilities" + "description": "Return GitLab items updated after the given date. ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z. It is not applicable for vulnerabilities" }, "updatedBefore": { "type": "string", "format": "date", - "description": "Return GitLab items updated before the given date. It is not applicable for vulnerabilities" + "description": "Return GitLab items updated before the given date. ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z. It is not applicable for vulnerabilities" }, "wip": { "type": "string", @@ -522,6 +522,8 @@ "devDependencies": { "@types/jest": "^26.0.14", "@types/node": "^13.7.0", + "@types/request-promise": "^4.1.46", + "@types/sinon": "^9.0.4", "@types/temp": "^0.8.34", "@types/vscode": "^1.41.0", "@typescript-eslint/eslint-plugin": "^3.7.0", diff --git a/scripts/create_workspace_for_test_debugging.js b/scripts/create_workspace_for_test_debugging.js index 6764289aba2989c98a8adf224d6614fb2f1c099c..3edbfef07ae2a38c1202db7b13f9afc2cea98e50 100755 --- a/scripts/create_workspace_for_test_debugging.js +++ b/scripts/create_workspace_for_test_debugging.js @@ -1,6 +1,15 @@ #!/usr/bin/env node // This script creates a temporary workspace that can be used for debugging integration tests +const { readFileSync, writeFileSync } = require('fs'); +const path = require('path'); const { default: createTmpWorkspace } = require('../out/create_tmp_workspace'); -createTmpWorkspace(false).then(console.log); +const PLACEHOLDER = ``; + +createTmpWorkspace(false).then(workspaceFolder => { + const launchPath = path.resolve(__dirname, '../.vscode/launch.json'); + const tasksContent = readFileSync(launchPath, 'UTF-8'); + const tasks = tasksContent.replace(PLACEHOLDER, workspaceFolder); + writeFileSync(launchPath, tasks); +}); diff --git a/src/errors/api_error.js b/src/errors/api_error.js deleted file mode 100644 index e6930464e2eda49e4a97f5417f98d4e73c1082fb..0000000000000000000000000000000000000000 --- a/src/errors/api_error.js +++ /dev/null @@ -1,32 +0,0 @@ -const { StatusCodeError } = require('request-promise/errors'); -const { prettyJson, stackToArray } = require('./common'); - -class ApiError extends Error { - constructor(error, action) { - super(error); - this.action = action; - this.message = `API request failed when trying to ${this.action} because: ${error.message}`; - this.originalError = error; - } - - get requestDetails() { - if (!(this.originalError instanceof StatusCodeError)) return {}; - const { method, url } = this.originalError.options; - const { response } = this.originalError; - return { - request: { method, url }, - response, - }; - } - - get details() { - const { message, stack } = this; - return prettyJson({ - message, - stack: stackToArray(stack), - ...this.requestDetails, - }); - } -} - -module.exports = { ApiError }; diff --git a/src/errors/api_error.ts b/src/errors/api_error.ts new file mode 100644 index 0000000000000000000000000000000000000000..5692360270a43b575c5956dbffc87ce237b548b2 --- /dev/null +++ b/src/errors/api_error.ts @@ -0,0 +1,40 @@ +import { StatusCodeError } from 'request-promise/errors'; +import { prettyJson, stackToArray, IDetailedError } from './common'; + +export class ApiError extends Error implements IDetailedError { + originalError: Error; + + action: string; + + message: string; + + constructor(error: Error, action: string) { + super(error.message); + this.action = action; + this.message = `API request failed when trying to ${this.action} because: ${error.message}`; + this.originalError = error; + } + + private get requestDetails() { + if (!(this.originalError instanceof StatusCodeError)) return {}; + const { method } = this.originalError.options; + // The url parameter exists, but the types are not complete + // eslint-disable-next-line + // @ts-ignore + const { url } = this.originalError.options; + const { response } = this.originalError; + return { + request: { method, url }, + response, + }; + } + + get details(): string { + const { message, stack } = this; + return prettyJson({ + message, + stack: stackToArray(stack), + ...this.requestDetails, + }); + } +} diff --git a/src/errors/user_friendly_error.ts b/src/errors/user_friendly_error.ts index 78bb0f0652f18dabf54a4518d5da99bf86e768a0..5d53c8d79fa2ac22a97708ffa0925d2c896ea246 100644 --- a/src/errors/user_friendly_error.ts +++ b/src/errors/user_friendly_error.ts @@ -3,9 +3,9 @@ import { prettyJson, stackToArray, IDetailedError } from './common'; export class UserFriendlyError extends Error implements IDetailedError { originalError: Error; - additionalInfo: string; + additionalInfo?: string; - constructor(message: string, originalError: Error, additionalInfo: string) { + constructor(message: string, originalError: Error, additionalInfo?: string) { super(message); this.originalError = originalError; this.additionalInfo = additionalInfo; diff --git a/src/gitlab_service.js b/src/gitlab_service.js index 5604c8372c006ca346a9febd7c9948b750cb8f42..dd2bfc27cd9cca1f5555df1cc53ebdef7bfc3440 100644 --- a/src/gitlab_service.js +++ b/src/gitlab_service.js @@ -2,7 +2,6 @@ const vscode = require('vscode'); const request = require('request-promise'); const fs = require('fs'); const { tokenService } = require('./services/token_service'); -const statusBar = require('./status_bar'); const { UserFriendlyError } = require('./errors/user_friendly_error'); const { ApiError } = require('./errors/api_error'); const { getCurrentWorkspaceFolder } = require('./services/workspace_service'); @@ -452,7 +451,6 @@ async function handlePipelineAction(action, workspaceFolder) { if (pipeline && project) { let endpoint = `/projects/${project.id}/pipelines/${pipeline.id}/${action}`; - let newPipeline = null; if (action === 'create') { const branchName = await createGitService(workspaceFolder).fetchTrackingBranchName(); @@ -461,16 +459,13 @@ async function handlePipelineAction(action, workspaceFolder) { try { const { response } = await fetch(endpoint, 'POST'); - newPipeline = response; + return response; } catch (e) { - handleError(new UserFriendlyError(`Failed to ${action} pipeline.`, e)); - } - - if (newPipeline) { - statusBar.refreshPipeline(); + throw new UserFriendlyError(`Failed to ${action} pipeline.`, e); } } else { vscode.window.showErrorMessage('GitLab Workflow: No project or pipeline found.'); + return undefined; } } diff --git a/src/pipeline_actions_picker.js b/src/pipeline_actions_picker.js index d0ee0a95dc5f2d4cd24efb89efd6ff12682d8f7c..3a70f4e79220458ee53548916ac5d555fd0cc806 100644 --- a/src/pipeline_actions_picker.js +++ b/src/pipeline_actions_picker.js @@ -1,6 +1,7 @@ const vscode = require('vscode'); const gitLabService = require('./gitlab_service'); const openers = require('./openers'); +const statusBar = require('./status_bar'); const { getCurrentWorkspaceFolderOrSelectOne } = require('./services/workspace_service'); async function showPicker() { @@ -33,7 +34,8 @@ async function showPicker() { return; } - gitLabService.handlePipelineAction(selected.action, workspaceFolder); + const newPipeline = await gitLabService.handlePipelineAction(selected.action, workspaceFolder); + if (newPipeline) statusBar.refreshPipeline(); } } diff --git a/test/create_tmp_workspace.ts b/test/create_tmp_workspace.ts index 01627917b4b495b02da54138ac0690baeb851bec..b554823d5bf11c4c0dd8dc08dcf68b9ef6ca9b18 100644 --- a/test/create_tmp_workspace.ts +++ b/test/create_tmp_workspace.ts @@ -2,12 +2,7 @@ import * as temp from 'temp'; import simpleGit from 'simple-git'; import * as path from 'path'; import * as fs from 'fs'; -import { REMOTE } from './integration/test_infrastructure/constants'; - -const vsCodeSettings = { - 'gitlab.instanceUrl': 'https://test.gitlab.com', - 'files.enableTrash': false, -}; +import { REMOTE, DEFAULT_VS_CODE_SETTINGS } from './integration/test_infrastructure/constants'; async function createTempFolder(): Promise { return new Promise((resolve, reject) => { @@ -34,6 +29,6 @@ export default async function createTmpWorkspace(autoCleanUp = true): Promise { const sandbox = sinon.createSandbox(); before(async () => { - server = getServer(); + server = getServer([ + createTextEndpoint( + '/projects/278964/snippets/111/files/master/test.js/raw', + 'snippet content', + ), + createTextEndpoint( + '/projects/278964/snippets/222/files/master/test2.js/raw', + 'second blob content', + ), + graphql.query('GetSnippets', (req, res, ctx) => { + if (req.variables.projectPath === 'gitlab-org/gitlab') + return res(ctx.data(snippetsResponse)); + return res(ctx.data({ project: null })); + }), + ]); await tokenService.setToken(GITLAB_URL, 'abcd-secret'); }); diff --git a/test/integration/status_bar.test.js b/test/integration/status_bar.test.js index 61f150c8727b6a8a450a4bbfbf7990d5d1a98b6c..a072abb4f368f31826e501a82c84a64237d0624f 100644 --- a/test/integration/status_bar.test.js +++ b/test/integration/status_bar.test.js @@ -3,7 +3,9 @@ const sinon = require('sinon'); const vscode = require('vscode'); const statusBar = require('../../src/status_bar'); const { tokenService } = require('../../src/services/token_service'); -const getServer = require('./test_infrastructure/mock_server'); +const pipelinesResponse = require('./fixtures/rest/pipelines.json'); +const pipelineResponse = require('./fixtures/rest/pipeline.json'); +const { getServer, createJsonEndpoint } = require('./test_infrastructure/mock_server'); const { GITLAB_URL } = require('./test_infrastructure/constants'); describe('GitLab status bar', () => { @@ -18,7 +20,10 @@ describe('GitLab status bar', () => { }; before(async () => { - server = getServer(); + server = getServer([ + createJsonEndpoint('/projects/278964/pipelines?ref=master', pipelinesResponse), + createJsonEndpoint('/projects/278964/pipelines/47', pipelineResponse), + ]); await tokenService.setToken(GITLAB_URL, 'abcd-secret'); }); diff --git a/test/integration/test_infrastructure/constants.ts b/test/integration/test_infrastructure/constants.ts index 3cfcf36820b4c2cd55bfe7aff7f547a2e34dbd0c..e794438db094d9c46c2f7ad8721db7de634bd96d 100644 --- a/test/integration/test_infrastructure/constants.ts +++ b/test/integration/test_infrastructure/constants.ts @@ -4,3 +4,7 @@ export const REMOTE = { NAME: 'origin', URL: 'git@test.gitlab.com:gitlab-org/gitlab.git', }; +export const DEFAULT_VS_CODE_SETTINGS = { + 'gitlab.instanceUrl': GITLAB_URL, + 'files.enableTrash': false, +}; diff --git a/test/integration/test_infrastructure/helpers.js b/test/integration/test_infrastructure/helpers.js deleted file mode 100644 index 6a2d4e5a0c21ff3e615ca0854c2edf37eeb7431a..0000000000000000000000000000000000000000 --- a/test/integration/test_infrastructure/helpers.js +++ /dev/null @@ -1,23 +0,0 @@ -const vscode = require('vscode'); - -const createAndOpenFile = async testFileUri => { - const createFileEdit = new vscode.WorkspaceEdit(); - createFileEdit.createFile(testFileUri); - await vscode.workspace.applyEdit(createFileEdit); - await vscode.window.showTextDocument(testFileUri); -}; - -const closeAndDeleteFile = async testFileUri => { - await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); - const edit = new vscode.WorkspaceEdit(); - edit.deleteFile(testFileUri); - await vscode.workspace.applyEdit(edit); -}; - -const simulateQuickPickChoice = (sandbox, nthItem) => { - sandbox.stub(vscode.window, 'showQuickPick').callsFake(async options => { - return options[nthItem]; - }); -}; - -module.exports = { createAndOpenFile, closeAndDeleteFile, simulateQuickPickChoice }; diff --git a/test/integration/test_infrastructure/helpers.ts b/test/integration/test_infrastructure/helpers.ts new file mode 100644 index 0000000000000000000000000000000000000000..418045d9b95c16ec698bba81485370a513fb41ef --- /dev/null +++ b/test/integration/test_infrastructure/helpers.ts @@ -0,0 +1,30 @@ +import * as vscode from 'vscode'; +import { SinonSandbox } from 'sinon'; +import * as fs from 'fs'; +import * as assert from 'assert'; +import { DEFAULT_VS_CODE_SETTINGS } from './constants'; + +export const createAndOpenFile = async (testFileUri: vscode.Uri) => { + const createFileEdit = new vscode.WorkspaceEdit(); + createFileEdit.createFile(testFileUri); + await vscode.workspace.applyEdit(createFileEdit); + await vscode.window.showTextDocument(testFileUri); +}; + +export const closeAndDeleteFile = async (testFileUri: vscode.Uri) => { + await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); + const edit = new vscode.WorkspaceEdit(); + edit.deleteFile(testFileUri); + await vscode.workspace.applyEdit(edit); +}; + +export const simulateQuickPickChoice = (sandbox: SinonSandbox, nthItem: number) => { + sandbox.stub(vscode.window, 'showQuickPick').callsFake(async options => { + return (await options)[nthItem]; + }); +}; + +export const getWorkspaceFoder = () => { + const folders = vscode.workspace.workspaceFolders; + return folders && folders[0]?.uri.fsPath; +}; diff --git a/test/integration/test_infrastructure/mock_server.js b/test/integration/test_infrastructure/mock_server.js index 17b20f4b801d4bbb0485ffba10316fd35a995da1..c5af74fea9357658e5167a4a27c39e38713c5e2a 100644 --- a/test/integration/test_infrastructure/mock_server.js +++ b/test/integration/test_infrastructure/mock_server.js @@ -1,19 +1,24 @@ const { setupServer } = require('msw/node'); -const { rest, graphql } = require('msw'); +const { rest } = require('msw'); const { API_URL_PREFIX } = require('./constants'); const projectResponse = require('../fixtures/rest/project.json'); const versionResponse = require('../fixtures/rest/version.json'); -const openIssueResponse = require('../fixtures/rest/open_issue.json'); -const openMergeRequestResponse = require('../fixtures/rest/open_mr.json'); -const pipelinesResponse = require('../fixtures/rest/pipelines.json'); -const pipelineResponse = require('../fixtures/rest/pipeline.json'); -const snippetsResponse = require('../fixtures/graphql/snippets.json'); const createJsonEndpoint = (path, response) => rest.get(`${API_URL_PREFIX}${path}`, (req, res, ctx) => { return res(ctx.status(200), ctx.json(response)); }); +const createQueryJsonEndpoint = (path, queryResponseMap) => + rest.get(`${API_URL_PREFIX}${path}`, (req, res, ctx) => { + const response = queryResponseMap[req.url.search]; + if (!response) { + console.warn(`API call ${req.url.toString()} doesn't have a query handler.`); + return res(ctx.status(404)); + } + return res(ctx.status(200), ctx.json(response)); + }); + const createTextEndpoint = (path, response) => rest.get(`${API_URL_PREFIX}${path}`, (req, res, ctx) => { return res(ctx.status(200), ctx.text(response)); @@ -21,29 +26,15 @@ const createTextEndpoint = (path, response) => const notFoundByDefault = rest.get(/.*/, (req, res, ctx) => res(ctx.status(404))); -module.exports = () => { +const getServer = (handlers = []) => { const server = setupServer( createJsonEndpoint('/projects/gitlab-org%2Fgitlab', projectResponse), createJsonEndpoint('/version', versionResponse), - createJsonEndpoint('/projects/278964/merge_requests?scope=assigned_to_me&state=opened', [ - openMergeRequestResponse, - ]), - createJsonEndpoint('/projects/278964/issues?scope=assigned_to_me&state=opened', [ - openIssueResponse, - ]), - createJsonEndpoint('/projects/278964/pipelines?ref=master', pipelinesResponse), - createJsonEndpoint('/projects/278964/pipelines/47', pipelineResponse), - createTextEndpoint('/projects/278964/snippets/111/files/master/test.js/raw', 'snippet content'), - createTextEndpoint( - '/projects/278964/snippets/222/files/master/test2.js/raw', - 'second blob content', - ), - graphql.query('GetSnippets', (req, res, ctx) => { - if (req.variables.projectPath === 'gitlab-org/gitlab') return res(ctx.data(snippetsResponse)); - return res(ctx.data({ project: null })); - }), + ...handlers, notFoundByDefault, ); server.listen(); return server; }; + +module.exports = { getServer, createJsonEndpoint, createQueryJsonEndpoint, createTextEndpoint }; diff --git a/test/integration/tree_view.test.js b/test/integration/tree_view.test.js index dd440e3f805d7c5519807e06d97adb9281211a38..8e493baa6aff2950e86682b6d6ec5983ab8c7868 100644 --- a/test/integration/tree_view.test.js +++ b/test/integration/tree_view.test.js @@ -1,16 +1,88 @@ const assert = require('assert'); +const vscode = require('vscode'); const IssuableDataProvider = require('../../src/data_providers/issuable').DataProvider; const { tokenService } = require('../../src/services/token_service'); -const getServer = require('./test_infrastructure/mock_server'); +const openIssueResponse = require('./fixtures/rest/open_issue.json'); +const openMergeRequestResponse = require('./fixtures/rest/open_mr.json'); +const userResponse = require('./fixtures/rest/user.json'); +const { getServer, createQueryJsonEndpoint } = require('./test_infrastructure/mock_server'); const { GITLAB_URL } = require('./test_infrastructure/constants'); describe('GitLab tree view', () => { let server; let dataProvider; + const customQuerySettings = [ + { + name: 'Issues assigned to me', + type: 'issues', + scope: 'assigned_to_me', + state: 'opened', + noItemText: 'There is no issue assigned to you.', + }, + { + name: 'Merge requests assigned to me', + type: 'merge_requests', + scope: 'assigned_to_me', + state: 'opened', + noItemText: 'There is no MR assigned to you.', + }, + { + name: 'Custom GitLab Query for MR', + type: 'merge_requests', + scope: 'assigned_to_me', + state: 'all', + noItemText: 'There is no MR assigned to you.', + maxResults: 30, + labels: ['frontend', 'backend'], + milestone: '13.6', + author: 'johndoe', + assignee: 'johndoe', + search: 'query', + createdBefore: '2020-10-11T03:45:40Z', + createdAfter: '2018-11-01T03:45:40Z', + updatedBefore: '2020-10-30T03:45:40Z', + updatedAfter: '2018-11-01T03:45:40Z', + wip: 'yes', + orderBy: 'updated_at', + sort: 'asc', + }, + { + name: 'Custom GitLab Query for issues', + type: 'issues', + scope: 'assigned_to_me', + state: 'opened', + noItemText: 'There is no Issue assigned to you.', + confidential: true, + excludeLabels: ['backstage'], + excludeMilestone: ['13.5'], + excludeAuthor: 'johndoe', + excludeAssignee: 'johndoe', + excludeSearch: 'bug', + excludeSearchIn: 'description', + }, + ]; + before(async () => { - server = getServer(); + server = getServer([ + createQueryJsonEndpoint('/users', { + '?username=johndoe': [userResponse], + }), + createQueryJsonEndpoint('/projects/278964/merge_requests', { + '?scope=assigned_to_me&state=opened': [openMergeRequestResponse], + '?scope=assigned_to_me&state=all&labels=frontend,backend&milestone=13.6&author_id=7237201&assignee_id=7237201&search=query&created_before=2020-10-11T03&created_after=2018-11-01T03&updated_before=2020-10-30T03&updated_after=2018-11-01T03&wip=yes&order_by=updated_at&sort=asc&per_page=30': [ + { ...openMergeRequestResponse, title: 'Custom Query MR' }, + ], + }), + createQueryJsonEndpoint('/projects/278964/issues', { + '?scope=assigned_to_me&state=opened': [openIssueResponse], + '?scope=assigned_to_me&state=opened&confidential=true¬[labels]=backstage¬[milestone]=13.5¬[author_username]=johndoe¬[assignee_username]=johndoe¬[search]=bug¬[in]=description': [ + { ...openIssueResponse, title: 'Custom Query Issue' }, + ], + }), + ]); await tokenService.setToken(GITLAB_URL, 'abcd-secret'); + await vscode.workspace.getConfiguration().update('gitlab.customQueries', customQuerySettings); }); beforeEach(() => { @@ -21,6 +93,7 @@ describe('GitLab tree view', () => { after(async () => { server.close(); await tokenService.setToken(GITLAB_URL, undefined); + await vscode.workspace.getConfiguration().update('gitlab.customQueries', undefined); }); /** @@ -31,7 +104,7 @@ describe('GitLab tree view', () => { const [chosenCategory] = categories.filter(c => c.label === label); assert( chosenCategory, - `Can't open category ${label} because it's not present in ${categories}`, + `Can't open category ${label} because it's not present in ${categories.map(c => c.label)}`, ); return await dataProvider.getChildren(chosenCategory); } @@ -55,4 +128,18 @@ describe('GitLab tree view', () => { '!33824 · Web IDE - remove unused actions (mappings)', ); }); + + it('handles full custom query for MR', async () => { + const customMergeRequests = await openCategory('Custom GitLab Query for MR'); + + assert.strictEqual(customMergeRequests.length, 1); + assert.strictEqual(customMergeRequests[0].label, '!33824 · Custom Query MR'); + }); + + it('handles full custom query for issues', async () => { + const customMergeRequests = await openCategory('Custom GitLab Query for issues'); + + assert.strictEqual(customMergeRequests.length, 1); + assert.strictEqual(customMergeRequests[0].label, '#219925 · Custom Query Issue'); + }); });