Skip to content

Commit

Permalink
Add playback for the e2e run (#75)
Browse files Browse the repository at this point in the history
* Add playback for the e2e run

* test

* ..

* ..

* ..

* ..

* ..

* .

* ..

* ..

* ..

* ..

* ..

* ..

* ..

* ..

* ..

* ..

* ..

* ..

* ..

* ..

* ..

* ..

* ..

* ..

* ..

* ..

* ..

* ..

* ..

* ..

* ..

* ..

* ..

* ..

* ..

* ..

* ..

* ..

* ..

* ..

* ..

* ..

* ..

* ..

* ..

* meow

* ..

* ..

* ..

* ..

* ..

* Update src/tests/e2e.ts

Co-authored-by: meowjesty <[email protected]>

* Update src/tests/e2e.ts

Co-authored-by: meowjesty <[email protected]>

* Update src/tests/e2e.ts

Co-authored-by: meowjesty <[email protected]>

* ..

* ..

* ..

* ..

* workbrench

* ..

---------

Co-authored-by: meowjesty <[email protected]>
  • Loading branch information
infiniteregrets and meowjesty authored Oct 12, 2023
1 parent c3f1661 commit ecf8bf1
Show file tree
Hide file tree
Showing 6 changed files with 166 additions and 131 deletions.
20 changes: 18 additions & 2 deletions .github/workflows/reusable_e2e.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,28 @@ jobs:
echo "$KUBE_SERVICE"
echo "KUBE_SERVICE=$KUBE_SERVICE" >> "$GITHUB_ENV"
- uses: actions/setup-python@v4
with:
python-version: "3.10"
- name: Install dependencies
run: |
pip install flask
# we can't run chrome like apps in the CI, we use a virtual frame buffer:
# refer: http://elementalselenium.com/tips/38-headless
- name: Run vscode e2e in headless state
uses: coactions/setup-xvfb@v1
env:
POD_TO_SELECT: ${{ env.POD_TO_SELECT }}
KUBE_SERVICE: ${{ env.KUBE_SERVICE }}
run: |
sudo apt-get install -y xvfb x11-xserver-utils ffmpeg
/usr/bin/xvfb-run -s ":99 -auth /tmp/xvfb.auth -ac -screen 0 1920x1080x24" npm run test &
ffmpeg -loglevel panic -y -framerate 30 -f x11grab -video_size 1920x1080 -i :99 out.webm
wait -n
# the video starts at around ~2 minutes, before that you will see a black screen
- name: Upload video
if: always()
uses: actions/upload-artifact@v3
with:
run: npm run test
name: video
path: out.webm
10 changes: 0 additions & 10 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,9 @@ __pycache__
**/.DS_Store
target
build/
.mirrord

### go tests ###
*.go_test_app

### VSCode ###
vscode-ext/bin

### Intellij ###
intellij-ext/.gradle
intellij-ext/.idea
intellij-ext/.qodana
intellij-ext/build
intellij-ext/libmirrord_layer.*
intellij-ext/bin
.idea/
1 change: 1 addition & 0 deletions changelog.d/+video.internal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add video playback and bugfixes for the e2e
251 changes: 133 additions & 118 deletions src/tests/e2e.ts
Original file line number Diff line number Diff line change
@@ -1,141 +1,156 @@
import { expect } from "chai";
import { join } from "path";
import { VSBrowser, StatusBar, TextEditor, EditorView, ActivityBar, DebugView, InputBox, DebugToolbar } from "vscode-extension-tester";
import { VSBrowser, StatusBar, ActivityBar, DebugView, InputBox, DebugToolbar, BottomBarPanel, EditorView } from "vscode-extension-tester";
import get from "axios";


// This suite tests basic flow of mirroring traffic from remote pod
// - Enable mirrord -> Disable mirrord
// - Create mirrord config by pressing the gear icon
// - Set a breakpoint in the python file
// - Start debugging the python file
// - Select the pod from the QuickPick
// - Send traffic to the pod
// - Tests successfully exit if breakpoint is hit
const kubeService = process.env.KUBE_SERVICE;
const podToSelect = process.env.POD_TO_SELECT;

describe("mirrord sample flow test", function() {

this.timeout(1000000); // --> mocha tests timeout
this.bail(true); // --> stop tests on first failure

let browser: VSBrowser;
/**
* This suite tests basic flow of mirroring traffic from remote pod.
* - Enable mirrord
* - Start debugging the python file
* - Select the pod from the QuickPick
* - Send traffic to the pod
* - Tests successfully exit if "GET: Request completed" is found in the terminal
*/
describe("mirrord sample flow test", function () {

const testWorkspace = join(__dirname, '../../test-workspace');
const fileName = "app_flask.py";
const mirrordConfigPath = join(testWorkspace, '.mirrord/mirrord.json');
const defaultTimeout = 10000;
this.timeout("6 minutes"); // --> mocha tests timeout
this.bail(true); // --> stop tests on first failure

before(async function() {
console.log("podToSelect: " + podToSelect);
console.log("kubeService: " + kubeService);
let browser: VSBrowser;

expect(podToSelect).to.not.be.undefined;
expect(kubeService).to.not.be.undefined;
const testWorkspace = join(__dirname, '../../test-workspace');
const fileName = "app_flask.py";
const defaultTimeout = 10000; // = 10 seconds

browser = VSBrowser.instance;
// need to bring the flask app in open editors
await browser.openResources(testWorkspace, join(testWorkspace, fileName));
});
before(async function () {
console.log("podToSelect: " + podToSelect);
console.log("kubeService: " + kubeService);

it("enable mirrord", async function() {
const statusBar = new StatusBar();
await browser.driver.wait(async () => {
for (let button of await statusBar.getItems()) {
try {
if ((await button.getText()).startsWith('mirrord')) {
await button.click();
expect(podToSelect).to.not.be.undefined;
expect(kubeService).to.not.be.undefined;

return true;
}
} catch (e) { console.error(`Something went wrong ${e}`); }
}
}, defaultTimeout, "mirrord `enable` button not found -- timed out");
browser = VSBrowser.instance;

await browser.openResources(testWorkspace, join(testWorkspace, fileName));
await browser.waitForWorkbench();

await browser.driver.wait(async () => {
for (let button of await statusBar.getItems()) {
const ew = new EditorView();
try {
if ((await button.getText()).startsWith('mirrord')) {
return true;
}
} catch (e) { console.error(`Something went wrong ${e}`); }
}
}, defaultTimeout, "mirrord `disable` button not found -- timed out");
});

it("select pod from quickpick", async function() {
await setBreakPoint(fileName, browser, defaultTimeout);
await startDebugging();

const inputBox = await InputBox.create(defaultTimeout * 2);
// assertion that podToSelect is not undefined is done in "before" block
await browser.driver.wait(async () => {
if (!await inputBox.isDisplayed()) {
return false;
}

for (const pick of await inputBox.getQuickPicks()) {
let label = await pick.getLabel();

if (label === podToSelect) {
return true;
await ew.closeEditor('Welcome');
} catch (error) {
console.log("Welcome page is not displayed" + error)
// continue - Welcome page is not displayed
}

if (label === "Show Pods") {
await pick.select();
}
}

return false;
}, defaultTimeout * 2, "quickPick not found -- timed out");

await inputBox.selectQuickPick(podToSelect!);
});

it("wait for breakpoint to be hit", async function() {
const debugToolbar = await DebugToolbar.create(2 * defaultTimeout);
// waiting for breakpoint and sending traffic to pod are run in parallel
// however, traffic is sent after 10 seconds that we are sure the IDE is listening
// for breakpoints
await browser.driver.wait(async () => {
return await debugToolbar.isDisplayed();
}, 2 * defaultTimeout, "debug toolbar not found -- timed out");

sendTrafficToPod(debugToolbar);
debugToolbar.waitForBreakPoint();
});
await ew.openEditor('app_flask.py');
});

it("enable mirrord button", async function () {
const statusBar = new StatusBar();

await browser.driver.wait(async () => {
return await statusBar.isDisplayed();
});

// vscode refreshes the status bar on load and there is no deterministic way but to retry to click on
// the mirrord button after an interval
await browser.driver.wait(async () => {
let retries = 0;
while (retries < 3) {
try {
for (let button of await statusBar.getItems()) {
if ((await button.getText()).startsWith('mirrord')) {
await button.click();
return true;
}
}
} catch (e) {
if (e instanceof Error && e.name === 'StaleElementReferenceError') {
await new Promise(resolve => setTimeout(resolve, 1000));
retries++;
} else {
throw e;
}
}
}
throw new Error('Failed to click the button after multiple attempts');
}, defaultTimeout, "mirrord `enable` button not found -- timed out");
});

it("select pod from quickpick", async function () {
await startDebugging();
const inputBox = await InputBox.create(defaultTimeout * 2);
// assertion that podToSelect is not undefined is done in "before" block
await browser.driver.wait(async () => {
if (!await inputBox.isDisplayed()) {
return false;
}

for (const pick of await inputBox.getQuickPicks()) {
let label = await pick.getLabel();

if (label === podToSelect) {
return true;
}
// to pick up the podToSelect, we need to select the "Show Pods"
// from quickpick as pods are not displayed first
if (label === "Show Pods") {
await pick.select();
}
}

return false;
}, defaultTimeout * 2, "quickPick not found -- timed out");

await inputBox.selectQuickPick(podToSelect!);
});

it("wait for process to write to terminal", async function () {
const debugToolbar = await DebugToolbar.create(2 * defaultTimeout);
const panel = new BottomBarPanel();
await browser.driver.wait(async () => {
return await debugToolbar.isDisplayed();
}, 2 * defaultTimeout, "debug toolbar not found -- timed out");


let terminal = await panel.openTerminalView();

await browser.driver.wait(async () => {
const text = await terminal.getText();
return await terminal.isDisplayed() && text.includes("Press CTRL+C to quit");
}, 2 * defaultTimeout, "terminal text not found -- timed out");

await sendTrafficToPod();

await browser.driver.wait(async () => {
const text = await terminal.getText();
return text.includes("GET: Request completed");
}, defaultTimeout, "terminal text not found -- timed out");

});
});

async function sendTrafficToPod(debugToolbar: DebugToolbar) {
const response = await get(kubeService!!);
expect(response.status).to.equal(200);
expect(response.data).to.equal("OK - GET: Request completed\n");
}

// opens and sets a breakpoint in the given file
async function setBreakPoint(fileName: string, browser: VSBrowser, timeout: number, breakPoint: number = 9) {
const editorView = new EditorView();
await editorView.openEditor(fileName);
const currentTab = await editorView.getActiveTab();
expect(currentTab).to.not.be.undefined;
await browser.driver.wait(async () => {
const tabTitle = await currentTab?.getTitle();
if (tabTitle !== undefined) {
return tabTitle === fileName;
}
}, timeout, "editor tab title not found -- timed out");

const textEditor = new TextEditor();
await textEditor.toggleBreakpoint(breakPoint);
/**
* sends a GET request to the pod's nodePort
*/
async function sendTrafficToPod() {
const response = await get(kubeService!!);
expect(response.status).to.equal(200);
expect(response.data).to.equal("OK - GET: Request completed\n");
}

// starts debugging the current file with the provided configuration
// debugging starts from the "Run and Debug" button in the activity bar
/**
* starts debugging the current file with the provided configuration
* debugging starts from the "Run and Debug" button in the activity bar
*/
async function startDebugging(configurationFile: string = "Python: Current File") {
const activityBar = await new ActivityBar().getViewControl("Run and Debug");
expect(activityBar).to.not.be.undefined;
const debugView = await activityBar?.openView() as DebugView;
await debugView.selectLaunchConfiguration(configurationFile);
debugView.start();
}
const activityBar = await new ActivityBar().getViewControl("Run and Debug");
expect(activityBar).to.not.be.undefined;
const debugView = await activityBar?.openView() as DebugView;
await debugView.selectLaunchConfiguration(configurationFile);
await debugView.start();
}
10 changes: 10 additions & 0 deletions test-workspace/.mirrord/mirrord.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"feature": {
"network": {
"incoming": "mirror",
"outgoing": true
},
"fs": "read",
"env": true
}
}
5 changes: 4 additions & 1 deletion test-workspace/.vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@
"request": "launch",
"program": "${file}",
"console": "integratedTerminal",
"justMyCode": true
"justMyCode": true,
"env": {
"MIRRORD_CONFIG_FILE": "${workspaceFolder}/.mirrord/mirrord.json"
}
}
]
}

0 comments on commit ecf8bf1

Please sign in to comment.