[go: nahoru, domu]

Skip to content

Commit

Permalink
refactor(ui): improve the terminal connection close handling
Browse files Browse the repository at this point in the history
This commit is aimed to fix and remake some of the handling used to
manipulate the terminal screen, closing and opening.
  • Loading branch information
luannmoreira authored and gustavosbarreto committed Mar 13, 2024
1 parent 1d07576 commit 44f11d0
Show file tree
Hide file tree
Showing 4 changed files with 221 additions and 27 deletions.
39 changes: 17 additions & 22 deletions ui/src/components/Terminal/TerminalDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,22 @@
min-width="55vw"
@click:outside="close"
>
<v-card data-test="terminal-dialog" class="bg-v-theme-surface">
<v-card data-test="terminal-card" class="bg-v-theme-surface">
<v-card-title
class="text-h5 pa-4 bg-primary d-flex align-center justify-center"
>
Terminal
<v-spacer />
<v-icon @click="close()" class="bg-primary" size="24">mdi-close</v-icon>
<v-icon @click="close()" data-test="close-btn" class="bg-primary" size="24">mdi-close</v-icon>
</v-card-title>

<div class="mt-2" v-if="showLoginForm">
<v-tabs align-tabs="center" color="primary" v-model="tabActive">
<v-tab value="Password" @click="resetFieldValidation">Password</v-tab>
<v-tab value="Password" data-test="password-tab" @click="resetFieldValidation">Password</v-tab>
<v-tab
value="PublicKey"
value="PrivateKey"
@click="resetFieldValidation"
data-test="private-key-tab"
>Private Key</v-tab
>
</v-tabs>
Expand Down Expand Up @@ -61,7 +62,7 @@
label="Password"
required
variant="underlined"
data-test="password-text"
data-test="password-field"
:type="showPassword ? 'text' : 'password'"
@click:append-inner="showPassword = !showPassword"
/>
Expand All @@ -81,19 +82,18 @@
</v-form>
</v-window-item>

<v-window-item value="PublicKey">
<v-window-item value="PrivateKey">
<v-form
lazy-validation
@submit.prevent="connectWithPrivateKey()"
>
@submit.prevent="connectWithPrivateKey()">
<v-text-field
v-model="username"
:error-messages="usernameError"
label="Username"
autofocus
variant="underlined"
:validate-on-blur="true"
data-test="username-field"
data-test="username-field-pk"
/>

<v-select
Expand All @@ -113,7 +113,7 @@
color="primary"
class="mt-4"
variant="flat"
data-test="connect2-btn"
data-test="connect2-btn-pk"
>
Connect
</v-btn>
Expand All @@ -136,7 +136,6 @@ import {
ref,
computed,
watch,
nextTick,
onUnmounted,
} from "vue";
import { useField } from "vee-validate";
Expand Down Expand Up @@ -223,14 +222,7 @@ const nameOfPrivateKeys = computed(() => {
});
watch(showTerminal, (value) => {
if (!value) {
if (ws.value) ws.value.close();
if (xterm.value) {
xterm.value.dispose();
}
} else {
showLoginForm.value = true;
}
if (value) showLoginForm.value = true;
});
const encodeURLParams = (params: IParams) => Object.entries(params)
Expand All @@ -255,7 +247,6 @@ const connect = async (params: IConnectToTerminal) => {
const { token } = response.data;
showLoginForm.value = false;
nextTick(() => fitAddon.value.fit());
if (!xterm.value.element) {
xterm.value.open(terminal.value);
Expand Down Expand Up @@ -373,16 +364,20 @@ const connectWithPrivateKey = async () => {
};
const close = () => {
if (ws.value.OPEN) {
ws.value.close();
}
showTerminal.value = false;
store.dispatch("modal/toggleTerminal", "");
xterm.value.clear();
resetFieldValidation();
store.dispatch("modal/toggleTerminal", "");
};
onUnmounted(() => {
close();
});
defineExpose({ open });
defineExpose({ open, showTerminal, showLoginForm, encodeURLParams, connect, privateKey, xterm, fitAddon, ws, close });
</script>

<!-- <style lang="scss" scoped>
Expand Down
6 changes: 1 addition & 5 deletions ui/tests/components/Devices/DeviceList.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { mount, VueWrapper } from "@vue/test-utils";
import { createVuetify } from "vuetify";
import MockAdapter from "axios-mock-adapter";
import { expect, describe, it, beforeEach, afterEach, vi } from "vitest";
import { expect, describe, it, beforeEach, vi } from "vitest";
import { store, key } from "@/store";
import DeviceList from "@/components/Devices/DeviceList.vue";
import { envVariables } from "@/envVariables";
Expand Down Expand Up @@ -158,10 +158,6 @@ describe("Device List", () => {
});
});

afterEach(() => {
wrapper.unmount();
});

it("Is a Vue instance", () => {
expect(wrapper.vm).toBeTruthy();
});
Expand Down
194 changes: 194 additions & 0 deletions ui/tests/components/Terminal/TerminalDialog.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
import { flushPromises, DOMWrapper, mount, VueWrapper } from "@vue/test-utils";
import { createVuetify } from "vuetify";
import MockAdapter from "axios-mock-adapter";
import { expect, describe, it, beforeEach } from "vitest";
import { nextTick, watch } from "vue";
import { store, key } from "@/store";
import TerminalDialog from "@/components/Terminal/TerminalDialog.vue";
import { router } from "@/router";
import { namespacesApi, devicesApi } from "@/api/http";
import { SnackbarPlugin } from "@/plugins/snackbar";

const node = document.createElement("div");
node.setAttribute("id", "app");
document.body.appendChild(node);

const devices = [
{
uid: "a582b47a42d",
name: "39-5e-2a",
identity: {
mac: "00:00:00:00:00:00",
},
info: {
id: "linuxmint",
pretty_name: "Linux Mint 19.3",
version: "",
},
public_key: "----- PUBLIC KEY -----",
tenant_id: "fake-tenant-data",
last_seen: "2020-05-20T18:58:53.276Z",
online: false,
namespace: "user",
status: "accepted",
tags: ["test"],
},
{
uid: "a582b47a42e",
name: "39-5e-2b",
identity: {
mac: "00:00:00:00:00:00",
},
info: {
id: "linuxmint",
pretty_name: "Linux Mint 19.3",
version: "",
},
public_key: "----- PUBLIC KEY -----",
tenant_id: "fake-tenant-data",
last_seen: "2020-05-20T19:58:53.276Z",
online: true,
namespace: "user",
status: "accepted",
tags: ["test2"],
},
];

const members = [
{
id: "xxxxxxxx",
username: "test",
role: "owner",
},
];

const namespaceData = {
name: "test",
owner: "xxxxxxxx",
tenant_id: "fake-tenant-data",
members,
max_devices: 3,
devices_count: 3,
devices: 2,
created_at: "",
};

const authData = {
status: "",
token: "",
user: "test",
name: "test",
tenant: "fake-tenant-data",
email: "test@test.com",
id: "xxxxxxxx",
role: "owner",
};

const stats = {
registered_devices: 3,
online_devices: 1,
active_sessions: 0,
pending_devices: 0,
rejected_devices: 0,
};

describe("Terminal Dialog", async () => {
let wrapper: VueWrapper<InstanceType<typeof TerminalDialog>>;

const vuetify = createVuetify();

let mockNamespace: MockAdapter;
let mockDevices: MockAdapter;

beforeEach(async () => {
const el = document.createElement("div");
document.body.appendChild(el);

localStorage.setItem("tenant", "fake-tenant-data");

mockNamespace = new MockAdapter(namespacesApi.getAxios());
mockDevices = new MockAdapter(devicesApi.getAxios());
mockNamespace.onGet("http://localhost:3000/api/namespaces/fake-tenant-data").reply(200, namespaceData);
mockDevices.onGet("http://localhost:3000/api/devices?filter=&page=1&per_page=10&status=accepted").reply(200, devices);
mockDevices.onGet("http://localhost:3000/api/stats").reply(200, stats);

store.commit("auth/authSuccess", authData);
store.commit("namespaces/setNamespace", namespaceData);

wrapper = mount(TerminalDialog, {
global: {
plugins: [[store, key], vuetify, router, SnackbarPlugin],
config: {
errorHandler: () => { /* ignore global error handler */ },
},
},
});
});

it("Is a Vue instance", () => {
expect(wrapper.vm).toBeTruthy();
});

it("Renders the component", () => {
expect(wrapper.html()).toMatchSnapshot();
});

it("Data is defined", () => {
expect(wrapper.vm.$data).toBeDefined();
});

it("Renders the component table", async () => {
await wrapper.setProps({ enableConnectButton: true, enableConsoleIcon: true, uid: "a582b47a42d", online: true, show: true });

expect(wrapper.find('[data-test="connect-btn"]').exists()).toBe(true);

const dialog = new DOMWrapper(document.body);

await flushPromises();

await wrapper.findComponent('[data-test="connect-btn"]').trigger("click");

expect(dialog.find('[data-test="terminal-card"]').exists()).toBe(true);
expect(dialog.find('[data-test="close-btn"]').exists()).toBe(true);
expect(dialog.find('[data-test="password-tab"]').exists()).toBe(true);
expect(dialog.find('[data-test="private-key-tab"]').exists()).toBe(true);
expect(dialog.find('[data-test="username-field"]').exists()).toBe(true);
expect(dialog.find('[data-test="password-field"]').exists()).toBe(true);
expect(dialog.find('[data-test="connect2-btn"]').exists()).toBe(true);

// Change tab to Private Key authentication
await dialog.find('[data-test="private-key-tab"]').trigger("click");

expect(dialog.find('[data-test="username-field-pk"]').exists()).toBe(true);
expect(dialog.find('[data-test="privatekeys-select"]').exists()).toBe(true);
expect(dialog.find('[data-test="connect2-btn-pk"]').exists()).toBe(true);
});

it("sets showLoginForm to true when showTerminal changes to true", async () => {
await watch(() => wrapper.vm.showTerminal, (value) => {
if (value) wrapper.vm.showLoginForm = true;
});

wrapper.vm.showTerminal = true;

await nextTick();

expect(wrapper.vm.showLoginForm).toBe(true);
});

it("encodes URL params correctly", () => {
const params = { key1: "value1", key2: "value2" };
const encodedParams = wrapper.vm.encodeURLParams(params);

expect(encodedParams).toBe("key1=value1&key2=value2");
});

it("opens terminal and initializes xterm", () => {
wrapper.vm.open();

expect(wrapper.vm.showTerminal).toBe(true);
expect(wrapper.vm.privateKey).toBe("");
expect(wrapper.vm.xterm).toBeTruthy();
expect(wrapper.vm.fitAddon).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`Terminal Dialog > Renders the component 1`] = `
"<div>
<!--v-if-->
<!---->
<!---->
</div>"
`;

0 comments on commit 44f11d0

Please sign in to comment.