From 3fc6e28769a16411f385153cba9129e53cfdc6a2 Mon Sep 17 00:00:00 2001 From: Jakub Senko Date: Tue, 19 Mar 2024 20:45:33 +0100 Subject: [PATCH] ci: add a basic testsuite and github workflows - update dependencies - update docs and extension info --- .github/PULL_REQUEST_TEMPLATE.md | 23 ++-- .github/workflows/release.yaml | 123 +++++++++++++++++++++ .github/workflows/test.yaml | 46 ++++++++ .gitignore | 10 +- .vscodeignore | 17 +-- CONTRIBUTING.md | 42 ++++++-- package.json | 27 +++-- settings.json | 6 ++ src/test/.mocharc-debug.js | 3 + src/test/createAndViewArtifact-test.ts | 143 +++++++++++++++++++++++++ src/test/extensionInfo-test.ts | 26 +++++ src/test/resources/.gitkeep | 0 src/test/resources/avro1.json | 25 +++++ src/test/resources/avro2.json | 32 ++++++ tsconfig.json | 2 + 15 files changed, 488 insertions(+), 37 deletions(-) create mode 100644 .github/workflows/release.yaml create mode 100644 .github/workflows/test.yaml create mode 100644 settings.json create mode 100644 src/test/.mocharc-debug.js create mode 100644 src/test/createAndViewArtifact-test.ts create mode 100644 src/test/extensionInfo-test.ts create mode 100644 src/test/resources/.gitkeep create mode 100644 src/test/resources/avro1.json create mode 100644 src/test/resources/avro2.json diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index c44da61..e083e50 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,16 +1,23 @@ ## Description -Describe your changes in detail -## Related Issue + -If fixing a bug, there should be an issue describing it with steps to reproduce -Please link to the issue here: +## Related Issues -## Motivation and Context -Why is this change required? What problem does it solve? + ## How Has This Been Tested? + + + + \ No newline at end of file diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..d319b63 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,123 @@ +name: Release + +on: + workflow_dispatch: + inputs: + branch: + description: Branch to release from + required: true + default: main + release-version: + description: Version being released (e.g. 7.8.9) + required: true + next-version: + description: Version after the release (e.g. 7.8.10-dev) + required: true + # pre-release: + # type: boolean + # description: Create a pre-release version + # required: true + # default: true + changelog: + type: boolean + description: I have updated the CHANGELOG.md file + required: true + default: false + debug: + type: boolean + description: Debug with tmate on failure + required: true + default: true + +jobs: + test: + runs-on: ubuntu-latest + if: github.repository_owner == 'Apicurio' + steps: + - name: Check prerequisites + if: ${{ !inputs.changelog }} + run: | + exit 1 + + - name: Set up npm + uses: actions/setup-node@v4 + with: + node-version: 18 + + - name: Set up vsce + run: | + npm install -g @vscode/vsce + + - name: Set up ovsx + run: | + npm install -g ovsx + + - name: Checkout + run: | + git init + git config user.name apicurio-ci + git config user.email apicurio.ci@gmail.com + git remote add origin "https://apicurio-ci:${{ secrets.ACCESS_TOKEN }}@github.com/Apicurio/apicurio-registry-vscode-plugin.git" + git fetch + git checkout --track "origin/${{ inputs.branch }}" + + - name: Dependencies + run: | + npm install + + - name: Compile + run: | + npm run compile + + - name: Run Registry + run: | + echo "REGISTRY_CID=$(docker run -d -p 8080:8080 quay.io/apicurio/apicurio-registry-mem:2.5.x-snapshot)" >> "$GITHUB_ENV" + + - name: Test + run: | + xvfb-run -a npm run test + + - name: Stop Registry + run: | + docker stop "$REGISTRY_CID" + + # - name: Configure pre-release + # if: ${{ inputs.pre-release }} + # run: | + # echo "PRE_RELEASE=--pre-release" >> "$GITHUB_ENV" + + # - name: Configure release + # if: ${{ !inputs.pre-release }} + # run: | + # echo "PRE_RELEASE=" >> "$GITHUB_ENV" + + - name: Package + run: | + vsce package -m "Update to release version ${{ inputs.release-version }}" "${{ inputs.release-version }}" + echo "VSIX_FILE=apicurio-registry-explorer-${{ inputs.release-version }}.vsix" >> "$GITHUB_ENV" + + - name: Publish with vsce + run: | + vsce publish -i "$VSIX_FILE" -p "${{ secrets.VSCODE_MARKETPLACE_ACCESS_TOKEN }}" + + - name: Publish with ovsx + run: | + ovsx publish -i "$VSIX_FILE" -p "${{ secrets.OVSX_REGISTRY_ACCESS_TOKEN }}" + + - name: Next version + # if: ${{ !inputs.pre-release }} + run: | + npm version --no-git-tag-version ${{ inputs.next-version }} + git commit -m "Update to next development version ${{ inputs.next-version }}" + + - name: Push + # if: ${{ !inputs.pre-release }} + run: | + git push origin "${{ inputs.branch }}" + git push origin "v${{ inputs.release-version }}" + + # TODO: Create a GitHub release + + - name: Setup tmate session + if: failure() && inputs.debug + uses: mxschmitt/action-tmate@v3 diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml new file mode 100644 index 0000000..4e9ffe8 --- /dev/null +++ b/.github/workflows/test.yaml @@ -0,0 +1,46 @@ +name: Test + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + test: + runs-on: ubuntu-latest + if: github.repository_owner == 'Apicurio' + steps: + - name: Set up npm + uses: actions/setup-node@v4 + with: + node-version: 18 + + - name: Checkout + uses: actions/checkout@v3 + + - name: Dependencies + run: | + npm install + + - name: Compile + run: | + npm run compile + + - name: Run Registry + run: | + echo "REGISTRY_CID=$(docker run -d -p 8080:8080 quay.io/apicurio/apicurio-registry-mem:2.5.x-snapshot)" >> "$GITHUB_ENV" + + - name: Test + run: | + xvfb-run -a npm run test + + - name: Stop Registry + run: | + docker stop "$REGISTRY_CID" + +# - name: Setup tmate session +# if: failure() +# uses: mxschmitt/action-tmate@v3 diff --git a/.gitignore b/.gitignore index d9f7ed9..1f667de 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,8 @@ -.DS_Store -.vscode/settings.json *.vsix -out +.idea +.test-extensions +.trash~ +.vscode/settings.json node_modules -yarn.lock +out package-lock.json -.idea diff --git a/.vscodeignore b/.vscodeignore index 5ff3c19..287500b 100644 --- a/.vscodeignore +++ b/.vscodeignore @@ -1,9 +1,12 @@ -.vscode/** -.vscode-test/** -out/test/** -test/** -src/** -**/*.map +.github .gitignore +.idea +.test-extensions +.trash~ +.vscode +out/test +out/*.js.map +settings.json +src +test-resources tsconfig.json -vsc-extension-quickstart.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6c34e8d..aea3c52 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,7 +4,7 @@ When contributing to this repository, please first discuss the change you wish t Please note that this project is maintain on free personal time, be kind and do not except immediate answers. -## Prereqisites +## Prerequisites This extension is best developed in Visual Studio Code (VSC) itself. Even if you use other IDE to edit the code, VSC is required for debugging. @@ -22,12 +22,12 @@ To set up your VSC, install the following extensions: Run **ctrl+P** and then `ext install dbaeumer.vscode-eslint`. -### Run Apicurio Registry +### Apicurio Registry -You can run a local Apicurio Registry instance with docker: +You need Apicurio Registry during development, or to run the testsuite. You can run a local instance with docker: ```sh -docker run -it -p 8080:8080 apicurio/apicurio-registry-mem:latest-release +docker run -it -p 8080:8080 quay.io/apicurio/apicurio-registry-mem:2.5.x-snapshot ``` It will be available at http://localhost:8080. @@ -40,17 +40,45 @@ To package the extension for publishing, you need the `vsce` executable, which c npm install -g @vscode/vsce ``` -## Install +## Dependencies ```sh npm install ``` -## Developp +## Develop Use the **Run and Debug** panel to run and debug the extension. Make sure the **Run Extension** task is selected and press **F5**. -The task will compile the extention and launch a VSC instance with the extension loaded. +The task will compile the extension and launch a VSC instance with the extension loaded. + +## Compile + +```sh +npm run compile +``` + +or + +```sh +npm run vscode:prepublish +``` + +## Test + +To run the testsuite, an Apicurio Registry instance at `localhost:8080` is required. + +```sh +npm run test +``` + +or + +```sh +npm run test-no-timeout +``` + +without the default Mocha promise timeout. ## Package diff --git a/package.json b/package.json index 15dfbef..662688b 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,9 @@ { "name": "apicurio-registry-explorer", - "displayName": "Apicurio registry", - "description": "Explore Apicurio registry content.", + "displayName": "Apicurio Registry Explorer", + "description": "Explore Apicurio Registry content.", "version": "1.0.1", - "publisher": "apicurio", + "publisher": "Apicurio", "author": { "name": "Apicurio", "url": "https://www.apicur.io/" @@ -14,7 +14,7 @@ "url": "https://github.com/Apicurio/apicurio-registry-vscode-plugin.git" }, "engines": { - "vscode": "^1.57.0" + "vscode": "^1.82.0" }, "categories": [ "Visualization" @@ -222,15 +222,22 @@ "vscode:prepublish": "npm run compile", "compile": "tsc -p ./", "watch": "tsc -watch -p ./", - "lint": "eslint . --ext .ts,.tsx" + "lint": "eslint . --ext .ts,.tsx", + "test": "extest setup-and-run './out/test/*-test.js' --code_settings settings.json --extensions_dir .test-extensions", + "test-no-timeout": "extest setup-and-run './out/test/*-test.js' --code_settings settings.json --extensions_dir .test-extensions --mocha_config './src/test/.mocharc-debug.js'" }, "devDependencies": { - "@types/node": "^17.0.12", - "@types/vscode": "^1.52.0", - "@typescript-eslint/eslint-plugin": "^5.10.1", - "eslint": "^8.7.0", + "@types/chai": "^4.3.11", + "@types/mocha": "^10.0.6", + "@types/node": "^18.19.25", + "@types/vscode": "^1.82.0", + "@typescript-eslint/eslint-plugin": "^7.2.0", + "chai": "^4.4.1", + "eslint": "^8.57.0", + "mocha": "^10.3.0", "prettier": "3.2.4", - "typescript": "^4.5.5" + "typescript": "^5.3.3", + "vscode-extension-tester": "^7.2.0" }, "dependencies": { "http": "^0.0.1-security", diff --git a/settings.json b/settings.json new file mode 100644 index 0000000..f26e662 --- /dev/null +++ b/settings.json @@ -0,0 +1,6 @@ +{ + "typescript.updateImportsOnFileMove.enabled": "always", + "workbench.editor.enablePreview": true, + "git.autoRepositoryDetection": false, + "terminal.integrated.sendKeybindingsToShell": true +} \ No newline at end of file diff --git a/src/test/.mocharc-debug.js b/src/test/.mocharc-debug.js new file mode 100644 index 0000000..e194a8c --- /dev/null +++ b/src/test/.mocharc-debug.js @@ -0,0 +1,3 @@ +module.exports = { + timeout: 999999999 +} \ No newline at end of file diff --git a/src/test/createAndViewArtifact-test.ts b/src/test/createAndViewArtifact-test.ts new file mode 100644 index 0000000..a3b5608 --- /dev/null +++ b/src/test/createAndViewArtifact-test.ts @@ -0,0 +1,143 @@ +import { expect } from 'chai'; +import { + ActivityBar, + DefaultTreeSection, + InputBox, + Key, + SideBarView, + TextSetting, + VSBrowser, + Workbench, +} from 'vscode-extension-tester'; +import path from 'path'; + +describe('Create and view a new artifact', () => { + const GROUP_ID = 'foo'; + const ARTIFACT_ID = 'bar'; + const VERSION = '1'; + + const labels = (dialog: InputBox) => dialog.getQuickPicks().then((a) => Promise.all(a.map((b) => b.getLabel()))); + + const delay = (ms?: number) => VSBrowser.instance.driver.sleep(ms ? ms : 1000); + + let dialog: InputBox; + + before(async function () { + this.timeout(25_000); + + await VSBrowser.instance.openResources(path.join('src', 'test', 'resources')); + + const settings = await new Workbench().openSettings(); + const setting = (await settings.findSettingByID('apicurio.http.port')) as TextSetting; + + await setting.setValue('8080'); // TODO: Change the default? Profiles? + await delay(); + }); + + describe('Create a new artifact', () => { + before(async function () { + await new Workbench().executeCommand('Add artifact'); + dialog = await InputBox.create(); + }); + + it('creates new group ID', async () => { + expect(await dialog.getTitle()).equals('New or existing group ?'); + expect(await labels(dialog)).to.have.members(['NEW', 'EXISTING']); + await dialog.selectQuickPick('NEW'); + + expect(await dialog.getTitle()).equals('Create a new Group ID'); + await dialog.setText(GROUP_ID); + await dialog.sendKeys(Key.ENTER); + + expect(await dialog.getTitle()).equals('Confirm new Group ID'); + await dialog.setText(GROUP_ID); + await dialog.sendKeys(Key.ENTER); + }); + + it('selects artifact type', async () => { + expect(await dialog.getTitle()).equals('Choose an artifact type to push :'); + expect(await labels(dialog)).to.have.members([ + 'AVRO', + 'PROTOBUF', + 'JSON', + 'OPENAPI', + 'ASYNCAPI', + 'GRAPHQL', + 'KCONNECT', + 'WSDL', + 'XSD', + 'XML', + ]); + await dialog.selectQuickPick('AVRO'); + }); + + it('selects artifact ID', async () => { + expect(await dialog.getTitle()).equals('Artifact ID'); + await dialog.setText(ARTIFACT_ID); + await dialog.sendKeys(Key.ENTER); + }); + + it('selects version', async () => { + expect(await dialog.getTitle()).equals('Initial version'); + await dialog.setText(VERSION); + await dialog.sendKeys(Key.ENTER); + }); + + it('selects file', async () => { + expect(await dialog.getTitle()).equals('Search for file :'); + await dialog.setText('avro1.json'); + await dialog.sendKeys(Key.ENTER); + await dialog.selectQuickPick(0); + await delay(); + }); + + it('confirms', async () => { + expect(await dialog.getTitle()).to.match(/^Confirm/); + expect(await labels(dialog)).to.have.members(['yes', 'no']); + await dialog.selectQuickPick('yes'); + }); + }); + + describe('View the new artifact', () => { + let tree: DefaultTreeSection; + + it('opens apicurio explorer', async () => { + await (await new ActivityBar().getViewControl('Apicurio Explorer'))?.openView(); + + const view = new SideBarView(); + expect(await view.getTitlePart().getTitle()).to.equal('APICURIO EXPLORER'); + + tree = (await view.getContent().getSection('Apicurio Explorer')) as DefaultTreeSection; + expect(await tree.getTitle()).to.equal('Apicurio Explorer'); + expect(await Promise.all((await tree.getActions()).map((a) => a.getLabel()))).to.have.members([ + 'Refresh', + 'Search', + 'Collapse All', + 'More Actions...', + ]); + + await VSBrowser.instance.driver + .actions() + .move(await tree.getRect()) + .perform(); + await (await tree.getAction('Refresh')).click(); + }); + + it('checks the new artifact', async () => { + expect(await Promise.all((await tree.getVisibleItems()).map((a) => a.getLabel()))).to.have.members([ + GROUP_ID, + ]); + const parent = await tree.findItem(GROUP_ID, 1); + //console.log('>>> ' + await parent.getDescription()); // TODO + expect(await parent.getTooltip()).to.equal(GROUP_ID + ' '); // TODO + await parent.expand(); + + expect(await Promise.all((await parent.getChildren()).map((a) => a.getLabel()))).to.have.members([ + ARTIFACT_ID, + ]); + const child = await parent.findChildItem(ARTIFACT_ID); + expect(await child.getDescription()).to.equal('enabled'); + expect(await child.getTooltip()).to.equal('User'); + }); + }); +}); diff --git a/src/test/extensionInfo-test.ts b/src/test/extensionInfo-test.ts new file mode 100644 index 0000000..7f5e819 --- /dev/null +++ b/src/test/extensionInfo-test.ts @@ -0,0 +1,26 @@ +import { expect } from 'chai'; +import { ActivityBar, ExtensionsViewItem, ExtensionsViewSection } from 'vscode-extension-tester'; +import package_json from '../../package.json'; + +// TODO: This finds an already released extension. Skipping because of an info update. +describe.skip('Test extension info', () => { + + let extension: ExtensionsViewItem; + + before(async function() { + this.timeout(15000); + const view = await (await new ActivityBar().getViewControl('Extensions'))?.openView(); + const extensions = await view?.getContent().getSection('Installed') as ExtensionsViewSection; + extension = await extensions.findItem(`@installed ${package_json.displayName}`) as ExtensionsViewItem; + }); + + it('checks the extension info', async () => { + const author = await extension.getAuthor(); + const desc = await extension.getDescription(); + const version = await extension.getVersion(); + + expect(author).to.equal(package_json.publisher); + expect(desc).to.equal(package_json.description); + expect(version).to.equal(package_json.version); + }); +}); diff --git a/src/test/resources/.gitkeep b/src/test/resources/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/test/resources/avro1.json b/src/test/resources/avro1.json new file mode 100644 index 0000000..f251134 --- /dev/null +++ b/src/test/resources/avro1.json @@ -0,0 +1,25 @@ +{ + "namespace": "example.avro", + "type": "record", + "name": "User", + "fields": [ + { + "name": "name", + "type": "string" + }, + { + "name": "favorite_number", + "type": [ + "int", + "null" + ] + }, + { + "name": "favorite_color", + "type": [ + "string", + "null" + ] + } + ] +} diff --git a/src/test/resources/avro2.json b/src/test/resources/avro2.json new file mode 100644 index 0000000..cedc02b --- /dev/null +++ b/src/test/resources/avro2.json @@ -0,0 +1,32 @@ +{ + "namespace": "example.avro", + "type": "record", + "name": "User", + "fields": [ + { + "name": "name", + "type": "string" + }, + { + "name": "favorite_number", + "type": [ + "int", + "null" + ] + }, + { + "name": "favorite_color", + "type": [ + "string", + "null" + ] + }, + { + "name": "favorite_food", + "type": [ + "string", + "null" + ] + } + ] +} diff --git a/tsconfig.json b/tsconfig.json index 38ebadf..6f7f106 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,6 +6,8 @@ "outDir": "out", "sourceMap": true, "rootDir": "src", + "resolveJsonModule": true, + "esModuleInterop": true }, "exclude": ["node_modules", ".vscode-test"], }