My first e2e test and modifications to client to support it

This commit is contained in:
2020-03-31 20:05:27 +00:00
parent 8feb4375f9
commit 2211bf550f
7 changed files with 136 additions and 24 deletions

View File

@@ -1,3 +1,8 @@
{ {
"pluginsFile": "tests/e2e/plugins/index.js" "pluginsFile": "tests/e2e/plugins/index.js",
} "baseUrl": "http://localhost:8080",
"env": {
"adminusername": "manager",
"adminpassword": "l3tm3in"
}
}

View File

@@ -49,12 +49,6 @@ CURRENT TODOs
@@@@@@@@@@@ ROADMAP STAGE 2: @@@@@@@@@@@ ROADMAP STAGE 2:
todo: update all dependencies
- Widget form: After update now the controls show broken rule color for some reason and dirty checking isn't working as save doesn't enabled
- better test other stuff too, just did a quickie (looks like it's an issue with the rule processor for forms adn the dirty checking or input event)
- No errors in console, just acting weird
- Actually, it *knows* it's dirty because I get unsaved changes error, just doesn't enable the save button and displays as if there are broken rules but no text to go with
todo: Client automated testing todo: Client automated testing
- Need several as basis for all future, make sure it's right. - Need several as basis for all future, make sure it's right.

View File

@@ -7,6 +7,7 @@
"build": "vue-cli-service build", "build": "vue-cli-service build",
"test:unit": "vue-cli-service test:unit", "test:unit": "vue-cli-service test:unit",
"test:e2e": "vue-cli-service test:e2e", "test:e2e": "vue-cli-service test:e2e",
"test:front": "vue-cli-service test:e2e --url http://localhost:8080",
"lint": "vue-cli-service lint", "lint": "vue-cli-service lint",
"myLint": "npm run lint" "myLint": "npm run lint"
}, },

View File

@@ -19,13 +19,13 @@
:prepend-icon="item.icon" :prepend-icon="item.icon"
:value="false" :value="false"
:key="item.key" :key="item.key"
:data-cy="item.testid"
> >
<template v-slot:activator> <template v-slot:activator>
<!--group activator --> <!--group activator -->
<v-list-item-title>{{ item.title }}</v-list-item-title> <v-list-item-title>{{ item.title }}</v-list-item-title>
</template> </template>
<!-- TOP LEVEL HOLDER SUBITEMS --> <!-- TOP LEVEL HOLDER SUBITEMS -->
<template v-for="subitem in item.navItems"> <template v-for="subitem in item.navItems">
<template v-if="!subitem.route"> <template v-if="!subitem.route">
<!-- SECOND LEVEL HOLDER --> <!-- SECOND LEVEL HOLDER -->
@@ -64,7 +64,10 @@
<template v-else> <template v-else>
<!-- SECOND LEVEL ACTION --> <!-- SECOND LEVEL ACTION -->
<div class="pl-3" :key="subitem.key"> <div class="pl-3" :key="subitem.key">
<v-list-item :to="subitem.route"> <v-list-item
:to="subitem.route"
:data-cy="'nav' + subitem.route"
>
<v-list-item-action> <v-list-item-action>
<v-icon v-if="subitem.icon">{{ subitem.icon }}</v-icon> <v-icon v-if="subitem.icon">{{ subitem.icon }}</v-icon>
</v-list-item-action> </v-list-item-action>
@@ -107,7 +110,10 @@
fixed fixed
app app
> >
<v-app-bar-nav-icon @click.stop="drawer = !drawer"></v-app-bar-nav-icon> <v-app-bar-nav-icon
@click.stop="drawer = !drawer"
data-cy="navicon"
></v-app-bar-nav-icon>
<v-toolbar-title style="width: 300px" class="ml-0 pl-4"> <v-toolbar-title style="width: 300px" class="ml-0 pl-4">
<v-icon>{{ appBar.icon }}</v-icon <v-icon>{{ appBar.icon }}</v-icon
>&nbsp; >&nbsp;
@@ -132,7 +138,7 @@
<v-spacer></v-spacer> <v-spacer></v-spacer>
<v-menu bottom float-left> <v-menu bottom float-left>
<template v-slot:activator="{ on }"> <template v-slot:activator="{ on }">
<v-btn text icon v-on="on"> <v-btn text icon v-on="on" data-cy="contextmenu">
<v-icon>fa-ellipsis-v</v-icon> <v-icon>fa-ellipsis-v</v-icon>
</v-btn> </v-btn>
</template> </template>
@@ -152,6 +158,7 @@
:disabled="item.disabled" :disabled="item.disabled"
@click="clickMenuItem(item)" @click="clickMenuItem(item)"
v-bind:class="{ 'hidden-sm-and-up': item.surface }" v-bind:class="{ 'hidden-sm-and-up': item.surface }"
:data-cy="item.key"
> >
<v-list-item-action> <v-list-item-action>
<v-icon <v-icon

View File

@@ -1,12 +1,16 @@
/* Xeslint-disable */ /* Xeslint-disable */
function addNavItem(title, icon, route, navItems, key) { function addNavItem(title, icon, route, navItems, key, testid) {
if (!testid) {
testid = route;
}
window.$gz.store.commit("addNavItem", { window.$gz.store.commit("addNavItem", {
title, title,
icon, icon,
route, route,
navItems, navItems,
key: key key: key,
testid: testid
}); });
} }
@@ -157,7 +161,8 @@ export default function initialize() {
"fa-home", "fa-home",
undefined, undefined,
sub, sub,
key++ key++,
"home"
); );
} }
@@ -200,7 +205,8 @@ export default function initialize() {
"fa-address-book", "fa-address-book",
undefined, undefined,
sub, sub,
key++ key++,
"customer"
); );
} }
@@ -387,7 +393,8 @@ export default function initialize() {
"fa-toolbox", "fa-toolbox",
undefined, undefined,
sub, sub,
key++ key++,
"service"
); );
} }
@@ -467,7 +474,8 @@ export default function initialize() {
"fa-box", "fa-box",
undefined, undefined,
sub, sub,
key++ key++,
"inventory"
); );
} }
@@ -488,7 +496,8 @@ export default function initialize() {
"fa-store", "fa-store",
"/vendors", "/vendors",
[], [],
key++ key++,
"vendor"
); );
} }
@@ -516,7 +525,8 @@ export default function initialize() {
"fa-calculator", "fa-calculator",
undefined, undefined,
sub, sub,
key++ key++,
"accounting"
); );
} }
@@ -602,7 +612,8 @@ export default function initialize() {
"fa-user-tie", "fa-user-tie",
undefined, undefined,
sub, sub,
key++ key++,
"administration"
); );
} }
@@ -668,7 +679,8 @@ export default function initialize() {
"fa-server", "fa-server",
undefined, undefined,
sub, sub,
key++ key++,
"operations"
); );
} }
@@ -688,7 +700,8 @@ export default function initialize() {
"fa-vial", "fa-vial",
"/widgets", "/widgets",
[], [],
key++ key++,
"widgets"
); );
} }
@@ -730,7 +743,8 @@ export default function initialize() {
"fa-home", "fa-home",
undefined, undefined,
sub, sub,
key++ key++,
"homecustomer"
); );
} }
}) })

View File

@@ -0,0 +1,62 @@
// login just once using API
let user;
before(function fetchUser() {
cy.request("POST", "http://localhost:7575/api/v8/Auth", {
username: Cypress.env("adminusername"),
password: Cypress.env("adminpassword")
})
.its("body")
.then(res => {
user = res;
});
});
// but set the user before visiting the page
// so the app thinks it is already authenticated
beforeEach(function setUser() {
cy.visit("/", {
onBeforeLoad(win) {
// and before the page finishes loading
// set the user object in local storage
win.localStorage.setItem("user", JSON.stringify(user));
}
});
// the page should be opened and the user should be logged in
});
describe("The home page", () => {
it("Successfully loads the home page", () => {
cy.visit("/");
});
});
describe("JWT", () => {
it("makes authenticated request", () => {
// we can make authenticated request ourselves
// since we know the token
cy.request({
url: "http://localhost:7575/api/v8/User/1",
auth: {
bearer: user.token
}
})
.its("body")
.should("deep.equal", [
{
id: 1,
username: "test",
firstName: "Test",
lastName: "User"
}
]);
});
it("is logged in", () => {
cy.contains("Hi Test!").should("be.visible");
});
it("shows loaded user", () => {
// this user information came from authenticated XHR call
cy.contains("li", "Test User").should("be.visible");
});
});

View File

@@ -0,0 +1,29 @@
// https://docs.cypress.io/api/introduction/api.html
describe("Login", () => {
it("Successfully logs in navigate and log out", () => {
cy.visit("/login");
cy.get("input[name=username]")
.clear()
.type(Cypress.env("adminusername"));
// {enter} causes the form to submit
cy.get("input[name=password]")
.clear()
.type(`${Cypress.env("adminpassword")}{enter}`);
// we should be redirected to /dashboard
cy.url().should("include", "/home-dashboard");
//navigate and confirm
//open nav and home menu
cy.get("[data-cy=navicon]").click();
cy.get("[data-cy=home]").click();
cy.get("[data-cy='nav/home-user-settings']").click();
cy.url().should("include", "/home-user-settings");
cy.get("[data-cy=contextmenu]").click();
cy.get("[data-cy='app:logout']").click();
cy.url().should("include", "/login");
});
});