Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Create a basic integration test #15

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 102 additions & 20 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -1,34 +1,116 @@
name: Main
on:
- push
- pull_request_target
- push
- pull_request_target
jobs:
ci:
name: CI
runs-on: ubuntu-latest
strategy:
matrix:
os:
# - macos-latest
- ubuntu-latest
# - windows-latest
ruby:
- 3.1
# - 3.0
# - 2.7
runs-on: ${{ matrix.os }}
env:
CI: true
steps:
- uses: actions/checkout@master
- uses: actions/setup-node@v3
with:
node-version: 14.x
cache: yarn
- name: Compile
run: |
yarn install --frozen-lockfile
yarn compile
- uses: actions/checkout@master
- uses: actions/setup-node@v3
with:
node-version: 14.x
cache: yarn
- uses: ruby/setup-ruby@v1
with:
bundler-cache: true
ruby-version: ${{ matrix.ruby }}
- name: Install gem
run: gem install syntax_tree
- name: Compile extension
run: |
yarn install --frozen-lockfile
yarn compile
- name: Setup GUI Environment
run: |
sudo apt-get install -yq dbus-x11 ffmpeg > /dev/null
mkdir -p ~/bin
mkdir -p ~/var/run
cat <<EOF > ~/bin/xvfb-shim
#! /bin/bash
echo DISPLAY=\$DISPLAY >> ${GITHUB_ENV}
echo XAUTHORITY=\$XAUTHORITY >> ${GITHUB_ENV}
sleep 86400
EOF
chmod a+x ~/bin/xvfb-shim
dbus-launch >> ${GITHUB_ENV}
start-stop-daemon --start --quiet --pidfile ~/var/run/Xvfb.pid --make-pidfile --background --exec /usr/bin/xvfb-run -- ~/bin/xvfb-shim
echo -n "Waiting for Xvfb to start..."
while ! grep -q DISPLAY= ${GITHUB_ENV}; do
echo -n .
sleep 3
done
if: runner.os == 'Linux'
- name: Start Screen Recording
run: |
mkdir -p $PWD/videos-raw
no_close=--no-close # uncomment to see ffmpeg output (i.e. leave stdio open)
start-stop-daemon $no_close --start --quiet --pidfile ~/var/run/ffmpeg.pid --make-pidfile --background --exec /usr/bin/ffmpeg -- -nostdin -f x11grab -video_size 1280x1024 -framerate 10 -i ${DISPLAY}.0+0,0 $PWD/videos-raw/test.mp4
# pid=`cat ~/var/run/ffmpeg.pid`
# echo "Waiting for ffmpeg (pid $pid) to start recording (display $DISPLAY)..."
# while [ ! -f $PWD/videos-raw/test.mp4 ]; do
# echo -n .
# sleep 3
# done
if: runner.os == 'Linux'
- name: Cache VS Code Binary
id: vscode-test
uses: actions/cache@v3
with:
path: .vscode-test/
key: ${{ runner.os }}-vscode-test
# - name: SSH Debug Breakpoint
# uses: lhotari/action-upterm@v1
# with:
# limit-access-to-actor: true
- name: Run Tests
run: npm test
- name: Stop Screen Recording
run: |
start-stop-daemon --stop --pidfile ~/var/run/ffmpeg.pid
sleep 3
mkdir -p $PWD/videos
for f in $PWD/videos-raw/*.mp4; do
out=`basename $f`
ffmpeg -i $f -vf format=yuv420p $PWD/videos/$out
done
if: always() && runner.os == 'Linux'
- name: Archive Screen Recording
uses: actions/upload-artifact@v3
with:
name: videos
path: |
videos/
if: always() && runner.os == 'Linux'
- name: Teardown GUI Environment
run: |
start-stop-daemon --stop --pidfile ~/var/run/Xvfb.pid
kill $DBUS_SESSION_BUS_PID
if: always() && runner.os == 'Linux'
automerge:
name: AutoMerge
needs: ci
runs-on: ubuntu-latest
if: github.event_name == 'pull_request_target' && (github.actor == github.repository_owner || github.actor == 'dependabot[bot]')
steps:
- uses: actions/github-script@v6
with:
script: |
github.pulls.merge({
owner: context.payload.repository.owner.login,
repo: context.payload.repository.name,
pull_number: context.payload.pull_request.number
})
- uses: actions/github-script@v6
with:
script: |
github.pulls.merge({
owner: context.payload.repository.owner.login,
repo: context.payload.repository.name,
pull_number: context.payload.pull_request.number
})
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.token
.vscode-test
node_modules
*.vsix
out
Expand Down
13 changes: 13 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
{
"version": "0.2.0",
"configurations": [

{
"name": "Run Extension",
"type": "extensionHost",
Expand All @@ -17,5 +18,17 @@
],
"preLaunchTask": "${defaultBuildTask}"
},
{
"name": "Run Extension Tests",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}",
"--extensionTestsPath=${workspaceFolder}/out/test/suite/index"
],
"outFiles": ["${workspaceFolder}/out/test/**/*.js"],
"preLaunchTask": "${defaultBuildTask}"
}
]
}
6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,15 +79,21 @@
"compile": "tsc -p ./",
"package": "vsce package --yarn --githubBranch main",
"publish": "vsce publish --yarn --githubBranch main",
"test": "node ./out/test/runTest.js",
"vscode:prepublish": "yarn compile",
"watch": "tsc --watch -p ./"
},
"dependencies": {
"vscode-languageclient": "8.0.2"
},
"devDependencies": {
"@types/glob": "^7.1.1",
"@types/mocha": "^9.1.1",
"@types/node": "^18.0.0",
"@types/vscode": "^1.68.0",
"@vscode/test-electron": "^1.6.1",
"glob": "^7.1.4",
"mocha": "^9.1.1",
"typescript": "^4.7.4",
"vsce": "^2.9.2"
}
Expand Down
12 changes: 8 additions & 4 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ import Visualize from "./Visualize";
const promiseExec = promisify(exec);

// This object will get initialized once the language client is ready. It will
// get set back to null when the extension is deactivated.
let languageClient: LanguageClient | null = null;
// get set back to null when the extension is deactivated. It is exported for
// easier testing.
export let languageClient: LanguageClient | null = null;

// This is the expected top-level export that is called by VSCode when the
// extension is activated.
Expand Down Expand Up @@ -54,6 +55,10 @@ export async function activate(context: ExtensionContext) {
// This function is called when the extension is activated or when the
// language server is restarted.
async function startLanguageServer() {
if (languageClient) {
return; // preserve idempotency
}

// The top-level configuration group is syntaxTree. All of the configuration
// for the extension is under that group.
const config = workspace.getConfiguration("syntaxTree");
Expand Down Expand Up @@ -142,8 +147,6 @@ export async function activate(context: ExtensionContext) {
startLanguageServer();
break;
}
if (action === 'Restart') {
}
}
}

Expand All @@ -154,6 +157,7 @@ export async function activate(context: ExtensionContext) {
if (languageClient) {
outputChannel.appendLine("Stopping language server...");
await languageClient.stop();
languageClient = null;
}
}

Expand Down
24 changes: 24 additions & 0 deletions src/test/runTest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { runTests } from '@vscode/test-electron';
import * as path from 'path';

import { USER_DATA_DIR, WORKSPACE_DIR } from './suite/setup';

async function main() {
try {
// The folder containing the Extension Manifest package.json
// Passed to `--extensionDevelopmentPath`
const extensionDevelopmentPath = path.resolve(__dirname, '../../');

// The path to the extension test script
// Passed to --extensionTestsPath
const extensionTestsPath = path.resolve(__dirname, './suite/index');

// Download VS Code, unzip it and run the integration test
await runTests({ extensionDevelopmentPath, extensionTestsPath, launchArgs: ['--disable-extensions', '--disable-gpu', '--user-data-dir', USER_DATA_DIR, WORKSPACE_DIR] });
} catch (err) {
console.error('Failed to run tests');
process.exit(1);
}
}

main();
45 changes: 45 additions & 0 deletions src/test/suite/automation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import * as assert from 'assert';
import * as path from 'path';
import { TextEncoder } from 'util';

import { Uri, commands, window, workspace } from 'vscode';

import { WORKSPACE_DIR } from './setup';

export async function reset() {
await commands.executeCommand('workbench.action.closeAllEditors');
}

export async function createEditor(content: string) {
const filename = `${Math.random().toString().slice(2)}.rb`;
const uri = Uri.file(`${WORKSPACE_DIR}${path.sep}${filename}`);
await workspace.fs.writeFile(uri, new TextEncoder().encode(content));
await window.showTextDocument(uri);
assert.ok(window.activeTextEditor);
assert.equal(window.activeTextEditor.document.getText(), content);
return window.activeTextEditor;
}

export function findNewestEditor() {
return window.visibleTextEditors[window.visibleTextEditors.length - 1];
}

export function formatDocument() {
return commands.executeCommand('editor.action.formatDocument', 'ruby-syntax-tree.vscode-syntax-tree');
}

export function restart() {
return commands.executeCommand('syntaxTree.restart');
}

export function start() {
return commands.executeCommand('syntaxTree.start');
}

export function stop() {
return commands.executeCommand('syntaxTree.stop');
}

export function visualize() {
return commands.executeCommand('syntaxTree.visualize');
}
56 changes: 56 additions & 0 deletions src/test/suite/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import * as assert from 'assert';
import { before, beforeEach } from 'mocha';
import { State } from 'vscode-languageclient';

import * as auto from './automation';
import * as extension from '../../extension';

const UNFORMATTED = `class Foo; def bar; puts 'baz'; end; end`;

const FORMATTED = `class Foo
def bar
puts "baz"
end
end
`;

suite('Syntax Tree', () => {
beforeEach(auto.reset);

suite('lifecycle commands', () => {
test('start', async () => {
await auto.start();
assert.notEqual(extension.languageClient, null)
assert.equal(extension.languageClient?.state, State.Running)
});

test('stop', async () => {
await auto.start();
await auto.stop();
assert.equal(extension.languageClient, null)
})

test('restart', async () => {
await auto.restart();
assert.notEqual(extension.languageClient, null)
assert.equal(extension.languageClient?.state, State.Running)
})
});

suite('functional commands', () => {
before(auto.start);

test('format', async () => {
const editor = await auto.createEditor(UNFORMATTED);
await auto.formatDocument();
assert.equal(editor.document.getText(), FORMATTED);
});

test('visualize', async () => {
await auto.createEditor(UNFORMATTED);
await auto.visualize();
const editor = auto.findNewestEditor();
assert.match(editor.document.getText(), /^\(program/);
})
})
});
Loading