initialize testing
parent
74680522c9
commit
285c487cab
|
|
@ -0,0 +1,25 @@
|
|||
name: Coverage Testing (Pull)
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
permissions: read-all
|
||||
|
||||
jobs:
|
||||
Coverage:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '16'
|
||||
- name: Install Application
|
||||
run: |
|
||||
npm ci
|
||||
npm install -g mocha c8 cypress@10
|
||||
- name: Copy Test Config
|
||||
run: cp ./data/config.testing.js ./data/config.js
|
||||
- name: Initialize Database
|
||||
run: npm run init:cemetery:test
|
||||
- name: Run Coverage Testing
|
||||
run: c8 --reporter=lcov --reporter=text --reporter=text-summary mocha --timeout 10000 --exit
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
name: Coverage Testing
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
|
||||
permissions: read-all
|
||||
|
||||
jobs:
|
||||
Coverage:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '16'
|
||||
- name: Install Application
|
||||
run: |
|
||||
npm ci
|
||||
npm install -g mocha c8 cypress@10
|
||||
- name: Copy Test Config
|
||||
run: cp ./data/config.testing.js ./data/config.js
|
||||
- name: Initialize Database
|
||||
run: npm run init:cemetery:test
|
||||
- name: Run Coverage Testing
|
||||
run: c8 --reporter=lcov --reporter=text --reporter=text-summary mocha --timeout 10000 --exit
|
||||
env:
|
||||
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
|
||||
- name: Code Climate
|
||||
run: |
|
||||
curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./codeclimate-test-reporter
|
||||
chmod +x codeclimate-test-reporter
|
||||
./codeclimate-test-reporter before-build
|
||||
./codeclimate-test-reporter after-build -t lcov --exit-code $?
|
||||
env:
|
||||
CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }}
|
||||
- name: Codacy
|
||||
run: bash <(curl -Ls https://coverage.codacy.com/get.sh) report -r ./coverage/lcov.info
|
||||
env:
|
||||
CODACY_PROJECT_TOKEN: ${{ secrets.CODACY_PROJECT_TOKEN }}
|
||||
|
|
@ -1,6 +1,9 @@
|
|||
.nyc_output/
|
||||
bin/daemon/
|
||||
coverage/
|
||||
cypress/downloads/
|
||||
cypress/screenshots/
|
||||
cypress/videos/
|
||||
data/sessions/
|
||||
node_modules/
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
declare const _default: Cypress.ConfigOptions<any>;
|
||||
export default _default;
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
import { defineConfig } from "cypress";
|
||||
export default defineConfig({
|
||||
e2e: {
|
||||
"baseUrl": "http://localhost:7000",
|
||||
"specPattern": "cypress/e2e/**/*.cy.ts",
|
||||
"supportFile": false,
|
||||
"projectId": "xya1fn"
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
import { defineConfig } from "cypress";
|
||||
|
||||
|
||||
export default defineConfig({
|
||||
e2e: {
|
||||
"baseUrl": "http://localhost:7000",
|
||||
"specPattern": "cypress/e2e/**/*.cy.ts",
|
||||
"supportFile": false,
|
||||
"projectId": "xya1fn"
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1 @@
|
|||
export {};
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
import { logout } from "../../support/index.js";
|
||||
describe("Login Page", () => {
|
||||
before(logout);
|
||||
it("Has no detectable accessibility issues", () => {
|
||||
cy.injectAxe();
|
||||
cy.checkA11y();
|
||||
});
|
||||
it("Contains a login form", () => {
|
||||
cy.get("form").should("have.length", 1);
|
||||
});
|
||||
it("Contains a _csrf field", () => {
|
||||
cy.get("form [name='_csrf']").should("exist");
|
||||
});
|
||||
it("Contains a userName field", () => {
|
||||
cy.get("form [name='userName']").should("exist");
|
||||
});
|
||||
it("Contains a password field", () => {
|
||||
cy.get("form [name='password']")
|
||||
.should("have.length", 1)
|
||||
.invoke("attr", "type")
|
||||
.should("equal", "password");
|
||||
});
|
||||
it("Contains a help link", () => {
|
||||
cy.get("a").contains("help", {
|
||||
matchCase: false
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
import {
|
||||
logout
|
||||
} from "../../support/index.js";
|
||||
|
||||
|
||||
describe("Login Page", () => {
|
||||
|
||||
before(logout);
|
||||
|
||||
it("Has no detectable accessibility issues", () => {
|
||||
cy.injectAxe();
|
||||
cy.checkA11y();
|
||||
});
|
||||
|
||||
it("Contains a login form", () => {
|
||||
cy.get("form").should("have.length", 1);
|
||||
});
|
||||
|
||||
it("Contains a _csrf field", () => {
|
||||
cy.get("form [name='_csrf']").should("exist");
|
||||
});
|
||||
|
||||
it("Contains a userName field", () => {
|
||||
cy.get("form [name='userName']").should("exist");
|
||||
});
|
||||
|
||||
it("Contains a password field", () => {
|
||||
cy.get("form [name='password']")
|
||||
.should("have.length", 1)
|
||||
.invoke("attr", "type")
|
||||
.should("equal", "password");
|
||||
})
|
||||
|
||||
it("Contains a help link", () => {
|
||||
cy.get("a").contains("help", {
|
||||
matchCase: false
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
import "cypress-axe";
|
||||
export declare const logout: () => void;
|
||||
export declare const login: (userName: string) => void;
|
||||
export declare const ajaxDelayMillis = 800;
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
import "cypress-axe";
|
||||
Cypress.Cookies.defaults({
|
||||
preserve: ["_csrf", "lot-occupancy-system-user-sid"]
|
||||
});
|
||||
export const logout = () => {
|
||||
cy.visit("/logout");
|
||||
};
|
||||
export const login = (userName) => {
|
||||
cy.visit("/login");
|
||||
cy.get(".message")
|
||||
.contains("Testing", {
|
||||
matchCase: false
|
||||
});
|
||||
cy.get("form [name='userName']").type(userName);
|
||||
cy.get("form [name='password']").type(userName);
|
||||
cy.get("form").submit();
|
||||
cy.location("pathname").should("not.contain", "/login");
|
||||
cy.get(".navbar").should("have.length", 1);
|
||||
};
|
||||
export const ajaxDelayMillis = 800;
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
/* eslint-disable node/no-unpublished-import */
|
||||
|
||||
import "cypress-axe";
|
||||
|
||||
|
||||
Cypress.Cookies.defaults({
|
||||
preserve: ["_csrf", "lot-occupancy-system-user-sid"]
|
||||
});
|
||||
|
||||
|
||||
export const logout = (): void => {
|
||||
cy.visit("/logout");
|
||||
};
|
||||
|
||||
|
||||
export const login = (userName: string): void => {
|
||||
cy.visit("/login");
|
||||
|
||||
cy.get(".message")
|
||||
.contains("Testing", {
|
||||
matchCase: false
|
||||
});
|
||||
|
||||
cy.get("form [name='userName']").type(userName);
|
||||
cy.get("form [name='password']").type(userName);
|
||||
|
||||
cy.get("form").submit();
|
||||
|
||||
cy.location("pathname").should("not.contain", "/login");
|
||||
|
||||
// Logged in pages have a navbar
|
||||
cy.get(".navbar").should("have.length", 1);
|
||||
};
|
||||
|
||||
|
||||
export const ajaxDelayMillis = 800;
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
export declare const config: import("../types/configTypes.js").Config;
|
||||
export default config;
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
import { config as cemeteryConfig } from "./config.cemetery.ssm.js";
|
||||
export const config = Object.assign({}, cemeteryConfig);
|
||||
config.application.useTestDatabases = true;
|
||||
config.users = {
|
||||
testing: ["*testView", "*testUpdate", "*testAdmin"],
|
||||
canLogin: ["*testView", "*testUpdate", "*testAdmin"],
|
||||
canUpdate: ["*testUpdate"],
|
||||
isAdmin: ["*testAdmin"]
|
||||
};
|
||||
export default config;
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
import { config as cemeteryConfig } from "./config.cemetery.ssm.js";
|
||||
|
||||
export const config = Object.assign({}, cemeteryConfig);
|
||||
|
||||
config.application.useTestDatabases = true;
|
||||
|
||||
config.users = {
|
||||
testing: ["*testView", "*testUpdate", "*testAdmin"],
|
||||
canLogin: ["*testView", "*testUpdate", "*testAdmin"],
|
||||
canUpdate: ["*testUpdate"],
|
||||
isAdmin: ["*testAdmin"]
|
||||
};
|
||||
|
||||
export default config;
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -14,7 +14,10 @@
|
|||
"start": "cross-env NODE_ENV=production node ./bin/www",
|
||||
"dev:test": "cross-env NODE_ENV=dev DEBUG=lot-occupancy-system:* TEST_DATABASES=true nodemon ./bin/www.js",
|
||||
"dev:live": "cross-env NODE_ENV=dev DEBUG=lot-occupancy-system:* nodemon ./bin/www.js",
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"cy:open": "cypress open --config-file cypress.config.ts",
|
||||
"cy:run": "cypress run --config-file cypress.config.ts",
|
||||
"test": "cross-env NODE_ENV=dev DEBUG=lot-occupancy-system:* TEST_DATABASES=true mocha --timeout 30000 --exit",
|
||||
"coverage": "cross-env NODE_ENV=dev DEBUG=lot-occupancy-system:* TEST_DATABASES=true c8 --reporter=lcov --reporter=text --reporter=text-summary mocha --timeout 30000 --exit",
|
||||
"temp:legacy:importFromCsv": "cross-env NODE_ENV=dev DEBUG=lot-occupancy-system:* TEST_DATABASES=true node ./temp/legacy.importFromCsv.js",
|
||||
"temp:so:exportMaps": "node ./temp/so.exportMaps.js"
|
||||
},
|
||||
|
|
@ -78,6 +81,8 @@
|
|||
"@typescript-eslint/eslint-plugin": "^5.34.0",
|
||||
"@typescript-eslint/parser": "^5.34.0",
|
||||
"bulma": "^0.9.4",
|
||||
"cypress": "^10.6.0",
|
||||
"cypress-axe": "^1.0.0",
|
||||
"eslint": "^8.22.0",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
|
|
|
|||
|
|
@ -128,4 +128,15 @@ fieldset:enabled .is-hidden-enabled {
|
|||
|
||||
.modal-card {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
/*
|
||||
* Accessibility
|
||||
*/
|
||||
|
||||
$black-ter: hsl(0, 0%, 14%);
|
||||
|
||||
.control .button.is-static,
|
||||
.menu .menu-label {
|
||||
color: $black-ter;
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1 @@
|
|||
export {};
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
import * as assert from "assert";
|
||||
import { portNumber } from "./_globals.js";
|
||||
import { exec } from "child_process";
|
||||
import * as http from "http";
|
||||
import { app } from "../app.js";
|
||||
describe("lot-occupancy-system", () => {
|
||||
const httpServer = http.createServer(app);
|
||||
let serverStarted = false;
|
||||
before(() => {
|
||||
httpServer.listen(portNumber);
|
||||
httpServer.on("listening", () => {
|
||||
serverStarted = true;
|
||||
});
|
||||
});
|
||||
after(() => {
|
||||
try {
|
||||
httpServer.close();
|
||||
}
|
||||
catch (_a) {
|
||||
}
|
||||
});
|
||||
it("Ensure server starts on port " + portNumber.toString(), () => {
|
||||
assert.ok(serverStarted);
|
||||
});
|
||||
describe("Cypress tests", () => {
|
||||
it("should run Cypress tests", (done) => {
|
||||
let cypressCommand = "cypress run --config-file cypress.config.ts --browser chrome";
|
||||
if (process.env.CYPRESS_RECORD_KEY && process.env.CYPRESS_RECORD_KEY !== "") {
|
||||
cypressCommand += " --record";
|
||||
}
|
||||
const childProcess = exec(cypressCommand);
|
||||
childProcess.stdout.on("data", (data) => {
|
||||
console.log(data);
|
||||
});
|
||||
childProcess.stderr.on("data", (data) => {
|
||||
console.error(data);
|
||||
});
|
||||
childProcess.on("exit", (code) => {
|
||||
assert.ok(code === 0);
|
||||
done();
|
||||
});
|
||||
}).timeout(30 * 60 * 60 * 1000);
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
/* eslint-disable unicorn/filename-case */
|
||||
|
||||
import * as assert from "assert";
|
||||
|
||||
import {
|
||||
portNumber
|
||||
} from "./_globals.js";
|
||||
|
||||
import {
|
||||
exec
|
||||
} from "child_process";
|
||||
|
||||
import * as http from "http";
|
||||
import {
|
||||
app
|
||||
} from "../app.js";
|
||||
|
||||
|
||||
describe("lot-occupancy-system", () => {
|
||||
|
||||
const httpServer = http.createServer(app);
|
||||
|
||||
let serverStarted = false;
|
||||
|
||||
before(() => {
|
||||
|
||||
httpServer.listen(portNumber);
|
||||
|
||||
httpServer.on("listening", () => {
|
||||
serverStarted = true;
|
||||
});
|
||||
});
|
||||
|
||||
after(() => {
|
||||
try {
|
||||
httpServer.close();
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
});
|
||||
|
||||
it("Ensure server starts on port " + portNumber.toString(), () => {
|
||||
assert.ok(serverStarted);
|
||||
});
|
||||
|
||||
describe("Cypress tests", () => {
|
||||
|
||||
it("should run Cypress tests", (done) => {
|
||||
|
||||
let cypressCommand = "cypress run --config-file cypress.config.ts --browser chrome";
|
||||
|
||||
if (process.env.CYPRESS_RECORD_KEY && process.env.CYPRESS_RECORD_KEY !== "") {
|
||||
cypressCommand += " --record";
|
||||
}
|
||||
|
||||
const childProcess = exec(cypressCommand);
|
||||
|
||||
childProcess.stdout.on("data", (data) => {
|
||||
console.log(data);
|
||||
});
|
||||
|
||||
childProcess.stderr.on("data", (data) => {
|
||||
console.error(data);
|
||||
});
|
||||
|
||||
childProcess.on("exit", (code) => {
|
||||
assert.ok(code === 0);
|
||||
done();
|
||||
});
|
||||
}).timeout(30 * 60 * 60 * 1000);
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
/// <reference types="qs" />
|
||||
import type { Request } from "express";
|
||||
import type { Session } from "express-session";
|
||||
export declare const testView = "*testView";
|
||||
export declare const testUpdate = "*testUpdate";
|
||||
export declare const testAdmin = "*testAdmin";
|
||||
export declare const portNumber = 7000;
|
||||
export declare const fakeViewOnlySession: Session;
|
||||
export declare const fakeAdminSession: Session;
|
||||
export declare const fakeRequest: Request;
|
||||
export declare const fakeViewOnlyRequest: Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>> & {
|
||||
session: Session;
|
||||
};
|
||||
export declare const fakeAdminRequest: Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>> & {
|
||||
session: Session;
|
||||
};
|
||||
|
|
@ -0,0 +1,133 @@
|
|||
var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); }
|
||||
var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) {
|
||||
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
||||
var g = generator.apply(thisArg, _arguments || []), i, q = [];
|
||||
return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i;
|
||||
function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }
|
||||
function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }
|
||||
function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }
|
||||
function fulfill(value) { resume("next", value); }
|
||||
function reject(value) { resume("throw", value); }
|
||||
function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }
|
||||
};
|
||||
export const testView = "*testView";
|
||||
export const testUpdate = "*testUpdate";
|
||||
export const testAdmin = "*testAdmin";
|
||||
export const portNumber = 7000;
|
||||
export const fakeViewOnlySession = {
|
||||
id: "",
|
||||
cookie: undefined,
|
||||
destroy: undefined,
|
||||
regenerate: undefined,
|
||||
reload: undefined,
|
||||
resetMaxAge: undefined,
|
||||
save: undefined,
|
||||
touch: undefined,
|
||||
user: undefined
|
||||
};
|
||||
export const fakeAdminSession = {
|
||||
id: "",
|
||||
cookie: undefined,
|
||||
destroy: undefined,
|
||||
regenerate: undefined,
|
||||
reload: undefined,
|
||||
resetMaxAge: undefined,
|
||||
save: undefined,
|
||||
touch: undefined,
|
||||
user: undefined
|
||||
};
|
||||
export const fakeRequest = {
|
||||
[Symbol.asyncIterator]() { return __asyncGenerator(this, arguments, function* _a() { }); },
|
||||
_destroy: undefined,
|
||||
_read: undefined,
|
||||
aborted: undefined,
|
||||
accepted: undefined,
|
||||
accepts: undefined,
|
||||
acceptsCharsets: undefined,
|
||||
acceptsEncodings: undefined,
|
||||
acceptsLanguages: undefined,
|
||||
addListener: undefined,
|
||||
app: undefined,
|
||||
baseUrl: undefined,
|
||||
body: undefined,
|
||||
cookies: undefined,
|
||||
complete: undefined,
|
||||
connection: undefined,
|
||||
csrfToken: undefined,
|
||||
destroy: undefined,
|
||||
destroyed: undefined,
|
||||
emit: undefined,
|
||||
eventNames: undefined,
|
||||
fresh: undefined,
|
||||
get: undefined,
|
||||
getMaxListeners: undefined,
|
||||
header: undefined,
|
||||
headers: undefined,
|
||||
host: undefined,
|
||||
hostname: undefined,
|
||||
httpVersion: undefined,
|
||||
httpVersionMajor: undefined,
|
||||
httpVersionMinor: undefined,
|
||||
ip: undefined,
|
||||
ips: undefined,
|
||||
is: undefined,
|
||||
isPaused: undefined,
|
||||
listenerCount: undefined,
|
||||
listeners: undefined,
|
||||
method: undefined,
|
||||
off: undefined,
|
||||
on: undefined,
|
||||
once: undefined,
|
||||
originalUrl: undefined,
|
||||
param: undefined,
|
||||
params: undefined,
|
||||
path: undefined,
|
||||
pause: undefined,
|
||||
pipe: undefined,
|
||||
prependListener: undefined,
|
||||
prependOnceListener: undefined,
|
||||
protocol: undefined,
|
||||
push: undefined,
|
||||
query: undefined,
|
||||
range: undefined,
|
||||
rawHeaders: undefined,
|
||||
rawListeners: undefined,
|
||||
rawTrailers: undefined,
|
||||
read: undefined,
|
||||
readable: undefined,
|
||||
readableAborted: undefined,
|
||||
readableDidRead: undefined,
|
||||
readableEncoding: undefined,
|
||||
readableEnded: undefined,
|
||||
readableFlowing: undefined,
|
||||
readableLength: undefined,
|
||||
readableHighWaterMark: undefined,
|
||||
readableObjectMode: undefined,
|
||||
removeAllListeners: undefined,
|
||||
removeListener: undefined,
|
||||
resume: undefined,
|
||||
route: undefined,
|
||||
secure: undefined,
|
||||
session: undefined,
|
||||
sessionID: undefined,
|
||||
sessionStore: undefined,
|
||||
setEncoding: undefined,
|
||||
setMaxListeners: undefined,
|
||||
setTimeout: undefined,
|
||||
signedCookies: undefined,
|
||||
socket: undefined,
|
||||
stale: undefined,
|
||||
subdomains: undefined,
|
||||
trailers: undefined,
|
||||
unpipe: undefined,
|
||||
unshift: undefined,
|
||||
url: undefined,
|
||||
wrap: undefined,
|
||||
xhr: undefined
|
||||
};
|
||||
export const fakeViewOnlyRequest = Object.assign({}, fakeRequest, {
|
||||
session: fakeViewOnlySession
|
||||
});
|
||||
export const fakeAdminRequest = Object.assign({}, fakeRequest, {
|
||||
session: fakeAdminSession
|
||||
});
|
||||
|
|
@ -0,0 +1,144 @@
|
|||
import type {
|
||||
Request
|
||||
} from "express";
|
||||
|
||||
import type {
|
||||
Session
|
||||
} from "express-session";
|
||||
|
||||
|
||||
export const testView = "*testView";
|
||||
export const testUpdate = "*testUpdate";
|
||||
export const testAdmin = "*testAdmin";
|
||||
|
||||
|
||||
export const portNumber = 7000;
|
||||
|
||||
|
||||
export const fakeViewOnlySession: Session = {
|
||||
id: "",
|
||||
cookie: undefined,
|
||||
destroy: undefined,
|
||||
regenerate: undefined,
|
||||
reload: undefined,
|
||||
resetMaxAge: undefined,
|
||||
save: undefined,
|
||||
touch: undefined,
|
||||
user: undefined
|
||||
};
|
||||
|
||||
|
||||
export const fakeAdminSession: Session = {
|
||||
id: "",
|
||||
cookie: undefined,
|
||||
destroy: undefined,
|
||||
regenerate: undefined,
|
||||
reload: undefined,
|
||||
resetMaxAge: undefined,
|
||||
save: undefined,
|
||||
touch: undefined,
|
||||
user: undefined
|
||||
};
|
||||
|
||||
|
||||
export const fakeRequest: Request = {
|
||||
async *[Symbol.asyncIterator]() {},
|
||||
_destroy: undefined,
|
||||
_read: undefined,
|
||||
aborted: undefined,
|
||||
accepted: undefined,
|
||||
accepts: undefined,
|
||||
acceptsCharsets: undefined,
|
||||
acceptsEncodings: undefined,
|
||||
acceptsLanguages: undefined,
|
||||
addListener: undefined,
|
||||
app: undefined,
|
||||
baseUrl: undefined,
|
||||
body: undefined,
|
||||
cookies: undefined,
|
||||
complete: undefined,
|
||||
connection: undefined,
|
||||
csrfToken: undefined,
|
||||
destroy: undefined,
|
||||
destroyed: undefined,
|
||||
emit: undefined,
|
||||
eventNames: undefined,
|
||||
fresh: undefined,
|
||||
get: undefined,
|
||||
getMaxListeners: undefined,
|
||||
header: undefined,
|
||||
headers: undefined,
|
||||
host: undefined,
|
||||
hostname: undefined,
|
||||
httpVersion: undefined,
|
||||
httpVersionMajor: undefined,
|
||||
httpVersionMinor: undefined,
|
||||
ip: undefined,
|
||||
ips: undefined,
|
||||
is: undefined,
|
||||
isPaused: undefined,
|
||||
listenerCount: undefined,
|
||||
listeners: undefined,
|
||||
method: undefined,
|
||||
off: undefined,
|
||||
on: undefined,
|
||||
once: undefined,
|
||||
originalUrl: undefined,
|
||||
param: undefined,
|
||||
params: undefined,
|
||||
path: undefined,
|
||||
pause: undefined,
|
||||
pipe: undefined,
|
||||
prependListener: undefined,
|
||||
prependOnceListener: undefined,
|
||||
protocol: undefined,
|
||||
push: undefined,
|
||||
query: undefined,
|
||||
range: undefined,
|
||||
rawHeaders: undefined,
|
||||
rawListeners: undefined,
|
||||
rawTrailers: undefined,
|
||||
read: undefined,
|
||||
readable: undefined,
|
||||
readableAborted: undefined,
|
||||
readableDidRead: undefined,
|
||||
readableEncoding: undefined,
|
||||
readableEnded: undefined,
|
||||
readableFlowing: undefined,
|
||||
readableLength: undefined,
|
||||
readableHighWaterMark: undefined,
|
||||
readableObjectMode: undefined,
|
||||
removeAllListeners: undefined,
|
||||
removeListener: undefined,
|
||||
resume: undefined,
|
||||
route: undefined,
|
||||
secure: undefined,
|
||||
session: undefined,
|
||||
sessionID: undefined,
|
||||
sessionStore: undefined,
|
||||
setEncoding: undefined,
|
||||
setMaxListeners: undefined,
|
||||
setTimeout: undefined,
|
||||
signedCookies: undefined,
|
||||
socket: undefined,
|
||||
stale: undefined,
|
||||
subdomains: undefined,
|
||||
trailers: undefined,
|
||||
unpipe: undefined,
|
||||
unshift: undefined,
|
||||
url: undefined,
|
||||
wrap: undefined,
|
||||
xhr: undefined
|
||||
};
|
||||
|
||||
|
||||
export const fakeViewOnlyRequest =
|
||||
Object.assign({}, fakeRequest, {
|
||||
session: fakeViewOnlySession
|
||||
});
|
||||
|
||||
|
||||
export const fakeAdminRequest =
|
||||
Object.assign({}, fakeRequest, {
|
||||
session: fakeAdminSession
|
||||
});
|
||||
|
|
@ -0,0 +1 @@
|
|||
export {};
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
import * as assert from "assert";
|
||||
import fs from "fs";
|
||||
import { version } from "../version.js";
|
||||
describe("version", () => {
|
||||
it("has a version that matches the package.json", () => {
|
||||
const packageJSON = JSON.parse(fs.readFileSync("package.json", "utf8"));
|
||||
assert.strictEqual(version, packageJSON.version);
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
import * as assert from "assert";
|
||||
|
||||
import fs from "fs";
|
||||
|
||||
import {
|
||||
version
|
||||
} from "../version.js";
|
||||
|
||||
|
||||
describe("version", () => {
|
||||
|
||||
it("has a version that matches the package.json", () => {
|
||||
const packageJSON = JSON.parse(fs.readFileSync("package.json", "utf8"));
|
||||
assert.strictEqual(version, packageJSON.version);
|
||||
});
|
||||
});
|
||||
|
|
@ -42,6 +42,7 @@ interface ConfigApplication {
|
|||
logoURL?: string;
|
||||
httpPort?: number;
|
||||
userDomain?: string;
|
||||
useTestDatabases?: boolean;
|
||||
}
|
||||
interface ConfigSession {
|
||||
cookieName?: string;
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ interface ConfigApplication {
|
|||
logoURL ? : string;
|
||||
httpPort ? : number;
|
||||
userDomain ? : string;
|
||||
useTestDatabases ?: boolean;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
134
views/login.ejs
134
views/login.ejs
|
|
@ -15,75 +15,75 @@
|
|||
</head>
|
||||
|
||||
<body>
|
||||
<div class="columns is-vcentered is-centered has-min-page-height is-marginless">
|
||||
<div class="column is-half-widescreen is-two-thirds-desktop is-three-quarters-tablet">
|
||||
<div class="box mx-3 my-3">
|
||||
<div class="columns is-vcentered">
|
||||
<div class="column has-text-centered">
|
||||
<img src="<%= urlPrefix + configFunctions.getProperty("application.logoURL") %>" alt="" style="max-height:400px" />
|
||||
</div>
|
||||
<div class="column">
|
||||
<h1 class="title is-3 has-text-centered">
|
||||
<%= configFunctions.getProperty("application.applicationName") %>
|
||||
</h1>
|
||||
<form id="form--login" method="post" action="<%= urlPrefix %>/login">
|
||||
<input name="_csrf" type="hidden" value="<%= csrfToken %>" />
|
||||
<input name="redirect" type="hidden" value="<%= redirect %>" />
|
||||
|
||||
<div class="field has-addons">
|
||||
<div class="control">
|
||||
<span class="button is-static"><%= configFunctions.getProperty("application.userDomain") %>\</span>
|
||||
</div>
|
||||
<div class="control is-expanded">
|
||||
<input class="input" id="login--userName" name="userName" type="text" placeholder="User Name" value="<%= userName %>" aria-label="User Name" autofocus required />
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="sr-only" for="login--password">Password</label>
|
||||
<div class="control has-icons-left has-tooltip-right" data-tooltip="Password" >
|
||||
<input class="input" id="login--password" name="password" type="password" placeholder="Password" required />
|
||||
<span class="icon is-small is-left">
|
||||
<i class="fas fa-key" aria-hidden="true"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<% if (useTestDatabases) { %>
|
||||
<div class="message is-small is-warning">
|
||||
<p class="message-body has-text-centered">
|
||||
Testing databases in use!
|
||||
</p>
|
||||
</div>
|
||||
<% } %>
|
||||
<div class="level is-mobile">
|
||||
<div class="level-left has-text-danger">
|
||||
<% if (message !== "") { %>
|
||||
<span class="icon">
|
||||
<i class="fas fa-exclamation-triangle" aria-hidden="true"></i>
|
||||
</span>
|
||||
<span><%= message %></span>
|
||||
<% } %>
|
||||
</div>
|
||||
<div class="level-right has-text-right">
|
||||
<button class="button is-link" type="submit">
|
||||
<span class="icon">
|
||||
<i class="fas fa-sign-in-alt" aria-hidden="true"></i>
|
||||
</span>
|
||||
<span>Log In</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<hr />
|
||||
<div class="has-text-right has-text-grey is-size-7">
|
||||
Build <%= buildNumber %><br />
|
||||
<a class="has-text-grey" href="https://cityssm.github.io/general-licence-manager/" target="_blank" rel="nofollow noreferrer">Help</a>
|
||||
<a class="has-text-grey ml-4" href="https://github.com/cityssm/general-licence-manager" target="_blank" rel="noreferrer">GitHub</a>
|
||||
<div class="columns is-vcentered is-centered has-min-page-height is-marginless">
|
||||
<div class="column is-half-widescreen is-two-thirds-desktop is-three-quarters-tablet">
|
||||
<main class="box mx-3 my-3">
|
||||
<div class="columns is-vcentered">
|
||||
<div class="column has-text-centered">
|
||||
<img src="<%= urlPrefix + configFunctions.getProperty("application.logoURL") %>" alt="" style="max-height:400px" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="column">
|
||||
<h1 class="title is-3 has-text-centered">
|
||||
<%= configFunctions.getProperty("application.applicationName") %>
|
||||
</h1>
|
||||
<form id="form--login" method="post" action="<%= urlPrefix %>/login">
|
||||
<input name="_csrf" type="hidden" value="<%= csrfToken %>" />
|
||||
<input name="redirect" type="hidden" value="<%= redirect %>" />
|
||||
|
||||
<div class="field has-addons">
|
||||
<div class="control">
|
||||
<span class="button is-static"><%= configFunctions.getProperty("application.userDomain") %>\</span>
|
||||
</div>
|
||||
<div class="control is-expanded">
|
||||
<input class="input" id="login--userName" name="userName" type="text" placeholder="User Name" value="<%= userName %>" aria-label="User Name" autofocus required />
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="sr-only" for="login--password">Password</label>
|
||||
<div class="control has-icons-left has-tooltip-right" data-tooltip="Password" >
|
||||
<input class="input" id="login--password" name="password" type="password" placeholder="Password" required />
|
||||
<span class="icon is-small is-left">
|
||||
<i class="fas fa-key" aria-hidden="true"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<% if (useTestDatabases) { %>
|
||||
<div class="message is-small is-warning">
|
||||
<p class="message-body has-text-centered">
|
||||
Testing databases in use!
|
||||
</p>
|
||||
</div>
|
||||
<% } %>
|
||||
<div class="level is-mobile">
|
||||
<div class="level-left has-text-danger">
|
||||
<% if (message !== "") { %>
|
||||
<span class="icon">
|
||||
<i class="fas fa-exclamation-triangle" aria-hidden="true"></i>
|
||||
</span>
|
||||
<span><%= message %></span>
|
||||
<% } %>
|
||||
</div>
|
||||
<div class="level-right has-text-right">
|
||||
<button class="button is-link" type="submit">
|
||||
<span class="icon">
|
||||
<i class="fas fa-sign-in-alt" aria-hidden="true"></i>
|
||||
</span>
|
||||
<span>Log In</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<hr />
|
||||
<div class="has-text-right has-text-grey-dark is-size-7">
|
||||
Build <%= buildNumber %><br />
|
||||
<a class="has-text-grey-dark" href="https://cityssm.github.io/general-licence-manager/" target="_blank" rel="nofollow noreferrer">Help</a>
|
||||
<a class="has-text-grey-dark ml-4" href="https://github.com/cityssm/general-licence-manager" target="_blank" rel="noreferrer">GitHub</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
try {
|
||||
|
|
@ -92,4 +92,4 @@
|
|||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
Loading…
Reference in New Issue