diff --git a/.gitignore b/.gitignore index 560276d02d4a8d66d92a8cbdfae9b049dc7314b6..3931a38b0b3f1fc2c5de2eaa88a400f7f1b7439e 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ *.swo /.trash /pids +/vscode /logs /node_modules /doc @@ -18,4 +19,6 @@ docker-compose.yaml .editorconfig package-lock.json -*~ \ No newline at end of file +*~ +.#* +*# diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 610933e85ae52dcb4a6c1a89fc063043fd2d0ec7..7a05ed7461d24d83091e92d63b13794a47157642 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -10,8 +10,14 @@ variables: POSTGRES_USER: 'runner' POSTGRES_PASSWORD: '123mudar' POSTGRES_PORT: 5432 + DB_USER: 'runner' + DB_NAME: 'form-creator' + DB_HOST: 'postgres' + DB_PASSWORD: '123mudar' + DB_PORT: 5432 image_version: '' DOCKER_URL: 'dockerregistry.c3sl.ufpr.br:5000/c3sl/simmctic-form-creator-api' + GIT_SUBMODULE_STRATEGY: recursive cache: paths: @@ -31,12 +37,6 @@ run_test: - wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - - apt-get update -q -y - apt-get install -y postgresql-client-10 - - git clone --recurse-submodules https://gitlab.c3sl.ufpr.br/simmctic/form-creator/form-creator-database.git form-creator-database - - cd form-creator-database - - psql-manager/manager.sh create workspace - - psql-manager/manager.sh fixture workspace - - cd .. - - rm -rf form-creator-database - yarn install --frozen-lockfile --silent --non-interactive - ln -s config.env.example config/test.env - yarn test diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000000000000000000000000000000000..95d144f4ac179d72f76a19c9a3481eb053349e53 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "form-creator-database"] + path = form-creator-database + url = https://gitlab.c3sl.ufpr.br/simmctic/form-creator/form-creator-database.git \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index d16cd2784fcb55e9dcb8b1c5e24fc019982655de..177d6dcbfd5b7eff2bac0121992bad66f607c3c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,10 +4,128 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 1.2.0 - 19-02-2020 +### Changed +- Created a stable version with user + + +## 1.1.13 - 10-02-2020 +### Added +- Route to read Form Answer #66 (Richard Heise & Gianfranco) +- Method to get IDs from form answers from a form +- Read all answers from a form +- Scenario form test to read + + +## 1.1.12 - 04-02-2020 +### Added +- Route to update an user #65 (Richard Heise) +## Changed +- Opthandler can create user without hash +- UserQueryBuilder Update now needs an id +- UserOptions hash is not obrigatory + + +## 1.1.11 - 04-02-2020 +## Changed +- Form controller update route to verify if a user own the form #62 (Gianfranco) + + +## 1.1.10 - 03-02-2020 +### Added +- Route to list forms #61 (Richard Heise) +## Changed +- List from FormQueryBuilder now lists an user's forms + + +## 1.1.9 - 03-02-2020 +### Added +- Route to change an user's password #63 (Richard Heise) +## Changed +- Delete route now has the token validation + + +## 1.1.8 - 30-01-2020 +### Added +- Route to assign users to forms #60 (Richard Heise) +## Changed +- Route to write a form now has an extra stage in the waterfall +- This stage assigns the user to a form by ID + + +## 1.1.7 - 29-01-2020 +### Added +- Function to assign users to forms #54 (Gianfranco) +- Assign added to userQueryBuilder file + + +## 1.1.6 - 24-01-2020 +### Added +- Middleware to validate tokens #56 (Richard Heise) +- Route do delete an user #59 (Richard Heise) +- Delete control methods on UserQueryBuilder +## Changed +- Initial user tests are now on the form.spec.ts + + +## 1.1.5 - 22-01-2020 +### Added +- SubForm class #57 (Gianfranco) +- InputType SubForm +- FormQueryBuilder methods to handle with SubForms +- AnswerQueryBuilder methods to handle with SubForms answers +- OptHandler method to validate SubForm +- TestHandler method to compare two SubForms +- Database table for SubForms +## Changed +- Input class to contain a SubForm object +- OptHandler methods to handle SubForms Answers +- Scenario file with new objects to test SubForms +- TestHandler method to compare two SubFormsAnswer + + +## 1.1.4 - 19-12-2019 +### Added +- SignIn route to possibilitate logins #55 (Richard Heise) +- JWT (Json Web Token) library and its types to dependencies +- Tokens can be created once an user is considered valid +## Changed +- UserQueryBuilder has, now, a public function to verify if an email is in the DB +- The requires are now imports for padronization reasons + + +## 1.1.3 - 12-12-2019 +### Added +- UserCtrl class created to control the user routes #52 (Richard Heise) +- The bcrypt library and its types to dependencies +- User route tests +### Changed +- User Query Builder now has writeController to validate an unique email user in the database + + +## 1.1.2 - 25-11-2019 +### Added +- User class to create an user object #53 (Richard Heise) +- User Query Builder to write, read and update an user in the database +- User tests +- An user table in the database + + +## 1.1.1 - 17-10-2019 +### Added +- Fixture class to manage the database #49 (Gianfranco) +- Database module inside the api +- Before test to call fixture class +### Changed +- Database to work with usql-manager +- Config class to receive the new parameters + + ## 1.1.0 - 11-10-2019 ### Changed - Create a stable version + ## 1.0.10 - 10-10-2019 ### Added - Validation type DEPENDENCY #41 (Gianfranco) @@ -79,6 +197,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Create a stable version + ## 0.0.27 - 15-08-2019 ### Added - DbHandler methods to update database #32 (Gianfranco) diff --git a/Dockerfile b/Dockerfile index c0266c20f44ff59c5f6be6ab9f3d7d36c6348b08..7919602c0e2a14fad55bf089a83ddc074664a725 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,6 +7,14 @@ RUN mkdir -p $WORKSPACE # Change WORKSPACE owner RUN chown -R node:node $WORKSPACE +# Install psql +RUN apt-get update -q -y +RUN apt-get install wget gnupg -y +RUN echo "deb http://apt.postgresql.org/pub/repos/apt/ stretch-pgdg main" > /etc/apt/sources.list.d/pgdg.list +RUN wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - +RUN apt-get update -q -y +RUN apt-get install -y postgresql-client-10 + # Change running user USER node # This sets the context of where commands will be ran in and is documented @@ -21,8 +29,6 @@ COPY --chown=node:node package.json . COPY --chown=node:node yarn.lock . - - RUN yarn install --frozen-lockfile --silent --non-interactive # If you are building your code for production # RUN yarn install --production --frozen-lockfile --silent --non-interactive @@ -34,5 +40,4 @@ COPY --chown=node:node . . EXPOSE 3000 - CMD [ "yarn", "start" ] diff --git a/config/config.env.example b/config/config.env.example index a2e7fa3e23e85c33aba93ef67242ceaae306bf52..421aa7e22e520340ae85cb0a0b54f093001c86ad 100644 --- a/config/config.env.example +++ b/config/config.env.example @@ -7,3 +7,6 @@ # You should not modify this file. You should copy it and edit and use the copy. PORT=3000 + +# This should be a secret key to encode the informations on the token +JWT_KEY="secret_very_long_string_key" diff --git a/form-creator-database b/form-creator-database new file mode 160000 index 0000000000000000000000000000000000000000..6cd530d9f009f739a7f285efddae4d29377dad7c --- /dev/null +++ b/form-creator-database @@ -0,0 +1 @@ +Subproject commit 6cd530d9f009f739a7f285efddae4d29377dad7c diff --git a/package.json b/package.json index a7c3995a0af590232a2143781af3171f85b0c8b8..eb9fa23a2805c97b071fc15b9d6d9aa6b58e15c9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "form-creator-api", - "version": "1.1.0", + "version": "1.2.0", "description": "RESTful API used to manage and answer forms.", "main": "index.js", "scripts": { @@ -18,6 +18,7 @@ "license": "ISC", "dependencies": { "@types/async": "^2.0.50", + "@types/bcrypt": "^3.0.0", "@types/express": "^4.16.0", "@types/pg": "^7.4.13", "async": "^2.6.1", @@ -28,10 +29,13 @@ }, "devDependencies": { "@types/chai": "^4.1.7", + "@types/jsonwebtoken": "^8.3.5", "@types/mocha": "^5.2.5", "@types/supertest": "^2.0.7", + "bcrypt": "^3.0.7", "chai": "^4.2.0", "istanbul": "1.1.0-alpha.1", + "jsonwebtoken": "^8.5.1", "mocha": "^5.2.0", "supertest": "^3.3.0", "tslint": "^5.13.1", diff --git a/src/api/apiTypes.ts b/src/api/apiTypes.ts index 716e3f4a4f2b18c2134f72e973aad7120ce5968d..5ba95fb0ba54384cd153349e87a13fb399f51080 100644 --- a/src/api/apiTypes.ts +++ b/src/api/apiTypes.ts @@ -31,6 +31,7 @@ import { DbHandler } from "../utils/dbHandler"; * return a error the extension must be made. */ export interface Request extends express.Request { + userData: string | object; /** A Database handler instace */ db: DbHandler; } diff --git a/src/api/controllers/form.spec.ts b/src/api/controllers/form.spec.ts index 99653d75a22c50f720d5d15960f35595b6a685b8..71bb21869573eee882df01ce9a3d580184c0d148 100644 --- a/src/api/controllers/form.spec.ts +++ b/src/api/controllers/form.spec.ts @@ -33,33 +33,67 @@ import { Input, InputOptions, Validation } from "../../core/input"; import { InputUpdate, InputUpdateOptions } from "../../core/inputUpdate"; import { DbHandler } from "../../utils/dbHandler"; import { configs } from "../../utils/config"; +import { Fixture } from "../../../test/fixture"; +import { formScenario } from "../../../test/scenario"; -describe("API data controller", () => { +before(function (done): void { + const fix: Fixture = new Fixture (); - const dbhandler = new DbHandler(configs.poolconfig); - - it("should respond 200 when getting a list of forms", (done) => { + waterfall([ + (callback: (err: Error) => void) => { + fix.drop(callback); + }, + (callback: (err: Error) => void) => { + fix.create(callback); + }, + (callback: (err: Error) => void) => { + fix.fixture(callback); + } + ], (err) => { + done(err); + }); +}); + +export let testToken: string; + +describe("Initial test User", () => { + it ("Should respond 200 when signing up a valid user", (done) => { request(server) - .get("/form") + .post("/user/signUp") + .send({ + name: "Test_name" + , email: "test_email@test.com" + , hash: "Test_pw" + }) .expect(200) .expect((res: any) => { - expect(res.body).to.be.an("array"); - expect(res.body[0].id).to.be.equal(1); - expect(res.body[0].title).to.be.equal("Form Title 1"); - expect(res.body[0].description).to.be.equal("Form Description 1"); - - expect(res.body[1].id).to.be.equal(2); - expect(res.body[1].title).to.be.equal("Form Title 2"); - expect(res.body[1].description).to.be.equal("Form Description 2"); - - expect(res.body[2].id).to.be.equal(3); - expect(res.body[2].title).to.be.equal("Form Title 3"); - expect(res.body[2].description).to.be.equal("Form Description 3"); - + expect(res.body).to.be.an("object"); + expect(res.body.message).to.be.an("string"); + expect(res.body.message).to.be.equal("User registered with sucess."); }) .end(done); }); + + it("Should respond 200 when validating an user signIn", (done) => { + request(server) + .post("/user/signIn") + .send({ + email: "test_email@test.com" + , hash: "Test_pw" + }) + .expect(200) + .expect((res: any) => { + expect(res.body).to.be.an("object"); + expect(res.body.message).to.be.an("string"); + expect(res.body.message).to.be.equal("Authentication successful."); + testToken = res.body.token; + }) + .end(done); + }); +}) + +describe("API data controller - form", () => { it("should respond 200 when getting valid form", (done) => { request(server) @@ -69,54 +103,7 @@ describe("API data controller", () => { expect(res.body).to.be.an("object"); const form: Form = new Form(OptHandler.form(res.body)); - const inputObj1: InputOptions = { - placement: 0 - , description: "Description Question 1 Form 1" - , question: "Question 1 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [] - , id: 1 - }; - - - const inputObj2: InputOptions = { - placement: 1 - , description: "Description Question 2 Form 1" - , question: "Question 2 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: ValidationType.MAXCHAR, arguments: ["10"] } - , { type: ValidationType.MINCHAR, arguments: ["2"] } - ] - , id: 3 - }; - - const inputObj3: InputOptions = { - placement: 2 - , description: "Description Question 3 Form 1" - , question: "Question 3 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: ValidationType.REGEX, arguments: ["\\d{5}-\\d{3}"] } - , { type: ValidationType.MANDATORY, arguments: [] } - ] - , id: 2 - }; - - const formObj: FormOptions = { - id: 1 - , title: "Form Title 1" - , description: "Form Description 1" - , inputs: [ - inputObj1 - , inputObj2 - , inputObj3 - ] - }; - TestHandler.testForm(form, new Form( OptHandler.form(formObj))); + TestHandler.testForm(form, new Form( OptHandler.form(formScenario.validForm))); }) .end(done); @@ -128,7 +115,6 @@ describe("API data controller", () => { .expect(500) .expect((res: any) => { const message = "Form with id: '10' not found. Some error has occurred. Check error property for details."; - expect(res.body).to.be.an("object"); expect(res.body).to.have.property("error"); expect(res.body.message).to.be.equal(message); @@ -140,85 +126,28 @@ describe("API data controller", () => { request(server) .post("/form") - .send({ - title: 'Form Title 4' - , description: 'Form Description 4' - , inputs: [ - { - placement: 0 - , description: 'Description Question 1 Form 4' - , question: 'Question 1 Form 4' - , enabled: true - , type: InputType.TEXT - , validation: [] - } - , { - placement: 1 - , description: 'Description Question 2 Form 4' - , question: 'Question 2 Form 4' - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: 3, arguments: [ '10' ] } - , { type: 4, arguments: [ '2' ] } - ] - } - , { - placement: 2 - , description: 'Description Question 3 Form 4' - , question: 'Question 3 Form 4' - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: 1, arguments: [ '\\d{5}-\\d{3}' ] } - , { type: 2, arguments: [] } - ] - } - ] - }) + .set("Authorization", "bearer " + testToken) + .send(formScenario.formToPost) .expect(200) .end(done); }); + it("should respond 401 when failing to validate token", (done) => { + + request(server) + .post("/form") + .set("Authorization", "bearer " + "coronga") + .send(formScenario.formToPost) + .expect(401) + .end(done); + }); + it("should respond 500 when posting malformed form", (done) => { request(server) .post("/form") - .send({ - description: 'Form Description 4' - , inputs: [ - { - placement: 0 - , description: 'Description Question 1 Form 4' - , question: 'Question 1 Form 4' - , enabled: true - , type: InputType.TEXT - , validation: [] - } - , { - placement: 1 - , description: 'Description Question 2 Form 4' - , question: 'Question 2 Form 4' - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: 3, arguments: [ '10' ] } - , { type: 4, arguments: [ '2' ] } - ] - } - , { - placement: 2 - , description: 'Description Question 3 Form 4' - , question: 'Question 3 Form 4' - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: 1, arguments: [ '\\d{5}-\\d{3}' ] } - , { type: 2, arguments: [] } - ] - } - ] - }) + .set("Authorization", "bearer " + testToken) + .send(formScenario.formMissingTitle) .expect(500) .expect((res: any) => { const message = "Invalid Form. Check error property for details."; @@ -232,50 +161,11 @@ describe("API data controller", () => { it("should respond 200 when putting valid form update swap inputs", (done) => { request(server) .put("/form/1") - .send({ - id: 1 - , title: 'Form Title 1' - , description: 'Form Description 1' - , inputs: [ - { - placement: 0 - , description: 'Description Question 1 Form 1' - , question: 'Question 1 Form 1' - , enabled: true - , type: InputType.TEXT - , validation: [] - , id: 1 - } - , { - placement: 1 - , description: 'Description Question 2 Form 1' - , question: 'Question 2 Form 1' - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: 3, arguments: [ '10' ] } - , { type: 4, arguments: [ '2' ] } - ] - , id: 2 - } - , { - placement: 2 - , description: 'Description Question 3 Form 1' - , question: 'Question 3 Form 1' - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: 3, arguments: [ '10' ] } - , { type: 4, arguments: [ '2' ] } - ] - , id: 3 - } - ] - }) + .set("Authorization", "bearer " + testToken) + .send(formScenario.formToSwapInputs) .expect(200) .expect((res: any) => { - const message = "Updated" - expect(res.body.message).to.be.equal(message); + expect(res.body.message).to.be.equal(formScenario.msg); }) .end(done); }); @@ -283,77 +173,11 @@ describe("API data controller", () => { it("should respond 200 when putting valid form update add inputs", (done) => { request(server) .put("/form/3") - .send({ - id: 3 - , title: 'Form Title 3' - , description: 'Form Description 3' - , inputs: [ - { - placement: 0 - , description: 'Description Question 1 Form 3' - , question: 'Question 1 Form 3' - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: ValidationType.MANDATORY, arguments: [] } - ] - , id: 6 - } - , { - placement: 1 - , description: 'Description Question 2 Form 3' - , question: 'Question 2 Form 3' - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: 3, arguments: [ '10' ] } - , { type: 4, arguments: [ '2' ] } - ] - , id: 7 - } - , { - placement: 3 - , description: 'Description Question 4 Form 3' - , question: 'Question 4 Form 3' - , enabled: true - , type: InputType.CHECKBOX - , validation: [] - , sugestions: [ - { value: "Sugestion 1", placement: 0 } - , { value: "Sugestion 2", placement: 1 } - ] - , id: undefined - } - , { - placement: 4 - , description: 'Description Question 5 Form 3' - , question: 'Question 5 Form 3' - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: 3, arguments: [ '10' ] } - , { type: 4, arguments: [ '2' ] } - ] - , id: undefined - } - , { - placement: 5 - , description: 'Description Question 6 Form 3' - , question: 'Question 6 Form 3' - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: 3, arguments: [ '10' ] } - , { type: 4, arguments: [ '2' ] } - ] - , id: undefined - } - ] - }) + .set("Authorization", "bearer " + testToken) + .send(formScenario.formToAddInputs) .expect(200) .expect((res: any) => { - const message = "Updated" - expect(res.body.message).to.be.equal(message); + expect(res.body.message).to.be.equal(formScenario.msg); }) .end(done); }); @@ -361,17 +185,12 @@ describe("API data controller", () => { it("should respond 200 when putting valid form update remove inputs", (done) => { request(server) .put("/form/3") - .send({ - id: 3 - , title: 'Form Title 3' - , description: 'Form Description 3' - , inputs: [] - }) + .set("Authorization", "bearer " + testToken) + .send(formScenario.formToRemoveInputs) .expect(200) .expect((res: any) => { - const message = "Updated" - expect(res.body.message).to.be.equal(message); + expect(res.body.message).to.be.equal(formScenario.msg); }) .end(done); }); @@ -379,41 +198,12 @@ describe("API data controller", () => { it("should respond 200 when putting valid form update reenabled inputs", (done) => { request(server) .put("/form/3") - .send({ - id: 3 - , title: 'Form Title 3' - , description: 'Form Description 3' - , inputs: [ - { - placement: 0 - , description: 'Description Question 1 Form 3' - , question: 'Question 1 Form 3' - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: ValidationType.MANDATORY, arguments: [] } - ] - , id: 6 - } - , { - placement: 1 - , description: 'Description Question 2 Form 1' - , question: 'Question 2 Form 1' - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: 3, arguments: [ '10' ] } - , { type: 4, arguments: [ '2' ] } - ] - , id: 7 - } - ] - }) + .set("Authorization", "bearer " + testToken) + .send(formScenario.formToReenableInputs) .expect(200) .expect((res: any) => { - const message = "Updated" - expect(res.body.message).to.be.equal(message); + expect(res.body.message).to.be.equal(formScenario.msg); }) .end(done); @@ -422,53 +212,12 @@ describe("API data controller", () => { it("should respond 200 when putting valid form update changing the title", (done) => { request(server) .put("/form/4") - .send({ - id: 4 - , title: 'Title 4' - , description: 'Description 4' - , inputs: [ - { - placement: 0 - , description: 'Description Question 1 Form 4' - , question: 'Question 1 Form 4' - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: ValidationType.MANDATORY, arguments: [] } - ] - , id: 8 - } - , { - placement: 1 - , description: 'Description Question 2 Form 4' - , question: 'Question 2 Form 4' - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: 3, arguments: [ '10' ] } - , { type: 4, arguments: [ '2' ] } - ] - , id: 9 - } - , { - placement: 2 - , description: 'Description Question 3 Form 4' - , question: 'Question 3 Form 4' - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: 3, arguments: [ '10' ] } - , { type: 4, arguments: [ '2' ] } - ] - , id: 10 - } - ] - }) + .set("Authorization", "bearer " + testToken) + .send(formScenario.changeTitle) .expect(200) .expect((res: any) => { - const message = "Updated" - expect(res.body.message).to.be.equal(message); + expect(res.body.message).to.be.equal(formScenario.msg); }) .end(done); @@ -477,53 +226,12 @@ describe("API data controller", () => { it("should respond 200 when putting valid form update undo changes", (done) => { request(server) .put("/form/4") - .send({ - id: 4 - , title: 'Form Title 4' - , description: 'Form Description 4' - , inputs: [ - { - placement: 0 - , description: 'Description Question 1 Form 4' - , question: 'Question 1 Form 4' - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: ValidationType.MANDATORY, arguments: [] } - ] - , id: 8 - } - , { - placement: 1 - , description: 'Description Question 2 Form 4' - , question: 'Question 2 Form 4' - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: 3, arguments: [ '10' ] } - , { type: 4, arguments: [ '2' ] } - ] - , id: 9 - } - , { - placement: 2 - , description: 'Description Question 3 Form 4' - , question: 'Question 3 Form 4' - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: 3, arguments: [ '10' ] } - , { type: 4, arguments: [ '2' ] } - ] - , id: 10 - } - ] - }) + .set("Authorization", "bearer " + testToken) + .send(formScenario.undo) .expect(200) .expect((res: any) => { - const message = "Updated" - expect(res.body.message).to.be.equal(message); + expect(res.body.message).to.be.equal(formScenario.msg); }) .end(done); @@ -532,45 +240,8 @@ describe("API data controller", () => { it("should respond 500 when putting a valid form update for an inexistent form", (done) => { request(server) .put("/form/10") - .send({ - id: 1 - , title: 'Form Title 1' - , description: 'Form Description 1' - , inputs:[ - { - placement: 0 - , description: 'Description Question 1 Form 6' - , question: 'Question 1 Form 6' - , enabled: true - , type: InputType.TEXT - , validation: [] - , id: 1 - } - , { - placement: 1 - , description: 'Description Question 2 Form 6' - , question: 'Question 2 Form 6' - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: 3, arguments: [ '10' ] } - , { type: 4, arguments: [ '2' ] } - ] - , id: 2 - } - , { - placement: 2 - , description: 'Description Question 3 Form 6' - , question: 'Question 3 Form 6' - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: 3, arguments: [ '10' ] } - , { type: 4, arguments: [ '2' ] } - ] - } - ] - }) + .set("Authorization", "bearer " + testToken) + .send(formScenario.validUpdate) .expect(500) .expect((res: any) => { const message = "Could not update Form. Some error has ocurred. Check error property for details."; @@ -584,43 +255,8 @@ describe("API data controller", () => { it("should respond 500 when putting an malformed form update", (done) => { request(server) .put("/form/1") - .send({ - id: 1 - , inputs:[ - { - placement: 0 - , description: 'Description Question 1 Form 1' - , question: 'Question 1 Form 1' - , enabled: true - , type: InputType.TEXT - , validation: [] - , id: 1 - } - , { - placement: 1 - , description: 'Description Question 2 Form 1' - , question: 'Question 2 Form 1' - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: 3, arguments: [ '10' ] } - , { type: 4, arguments: [ '2' ] } - ] - , id: 2 - } - , { - placement: 2 - , description: 'Description Question 3 Form 1' - , question: 'Question 3 Form 1' - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: 3, arguments: [ '10' ] } - , { type: 4, arguments: [ '2' ] } - ] - } - ] - }) + .set("Authorization", "bearer " + testToken) + .send(formScenario.malformedUpdate) .expect(500) .expect((res: any) => { const message = "Invalid Form. Check error property for details."; @@ -631,4 +267,15 @@ describe("API data controller", () => { .end(done); }); + it("should respond 500 when putting valid form update swap inputs when user dont own the form", (done) => { + request(server) + .put("/form/5") + .set("Authorization", "bearer " + testToken) + .send(formScenario.formToSwapInputs) + .expect(500) + .expect((res: any) => { + expect(res.body.message).to.be.equal(formScenario.msg2); + }) + .end(done); + }); }); diff --git a/src/api/controllers/form.ts b/src/api/controllers/form.ts index 6e40357c03237def4bb4171ead70132834bb10be..5990cc2f8d6d5ddfe0e97940444ec183caf94e26 100644 --- a/src/api/controllers/form.ts +++ b/src/api/controllers/form.ts @@ -29,27 +29,6 @@ import { Request } from "../apiTypes"; export class FormCtrl { - public static list(req: Request, res: Response, next: NextFunction) { - - req.db.form.list((err: Error, forms?: Form[]) => { - if (err){ - res.status(500).json({ - message: "Could not list forms. Some error has occurred. Check error property for details.", - error: err - }); - return; - } - - const mappedForms = forms.map(form => ({ - id: form.id - , title: form.title - , description: form.description - })); - res.json(mappedForms); - return; - }); - } - public static read(req: Request, res: Response, next: NextFunction) { req.db.form.read(req.params.id, (err: Error, form?: Form) => { @@ -71,7 +50,7 @@ export class FormCtrl { let form: Form; try { form = new Form(OptHandler.form(req.body)); - } catch(e) { + } catch (e) { res.status(500).json({ message: "Invalid Form. Check error property for details.", error: e.message @@ -92,16 +71,34 @@ export class FormCtrl { , inputs: [] }; const formUpdate: FormUpdate = DiffHandler.diff(formResult, new Form (formOpts)); - + callback(null, formUpdate); }); }, - (formUpdate: FormUpdate, callback: (err: Error, formId: number) => void) => { + (formUpdate: FormUpdate, callback: (err: Error, formId?: number) => void) => { req.db.form.update(formUpdate, (err: Error) => { - callback(err, formUpdate.form.id); + + if (err) { + callback(err); + return; + } + + callback(null, formUpdate.form.id); + }); + }, + (formId: number, callback: (err: Error) => void) => { + /** req.userData has to be parsed into an object so we can get its ID */ + req.db.user.assign(Object(req.userData).id, formId, (err: Error) => { + + if (err) { + callback(err); + return; + } + + callback(null); }); } - ], (err, resultId) => { + ], (err) => { if (err) { res.status(500).json({ message: "Could not insert form. Some error has occurred. Check error property for details." @@ -109,10 +106,9 @@ export class FormCtrl { }); return; } - + res.json({ - id: resultId - , message: "Form added. Id on key 'id'" + message: "Form added." }); return; }); @@ -132,19 +128,30 @@ export class FormCtrl { return; } waterfall([ + (callback: (err: Error) => void) => { + req.db.form.list(Object(req.userData).id, (err: Error, forms?: Form[]) => { + if (err) { + callback(err); + return; + } + + const e: Error = new Error("User dont own this form."); + callback((forms.some((obj) => obj.id === Number(req.params.id))) ? null : e); + }); + }, (callback: (err: Error, result?: FormUpdate) => void) => { req.db.form.read(req.params.id, (err: Error, oldForm: Form) => { if (err) { callback(err); return; } - + const formUpdate: FormUpdate = DiffHandler.diff(newForm, oldForm); - + callback(null, formUpdate); }); }, - (formUpdate: FormUpdate, callback: (err: Error, formUpdateResult?: FormUpdate) => void) => { + (formUpdate: FormUpdate, callback: (err: Error, formResult?: Form) => void) => { req.db.form.update(formUpdate, callback); } ], (err) => { diff --git a/src/api/controllers/formAnswer.spec.ts b/src/api/controllers/formAnswer.spec.ts index cda55f53579940805607b369325fa8954da09416..b92c7cf54df2b248a94ff3e1bb124a0438c494bb 100644 --- a/src/api/controllers/formAnswer.spec.ts +++ b/src/api/controllers/formAnswer.spec.ts @@ -21,12 +21,16 @@ import * as request from "supertest"; import { expect } from "chai"; +import { formAnswerScenario, dbHandlerScenario } from "../../../test/scenario"; import * as server from "../../main"; import { EnumHandler,InputType, ValidationType } from "../../utils/enumHandler"; import { TestHandler } from "../../utils/testHandler"; import { OptHandler } from "../../utils/optHandler"; import { Form, FormOptions } from "../../core/form"; import { Input, InputOptions, Validation } from "../../core/input"; +import { testToken } from "./form.spec"; +import { FormAnswer, FormAnswerOptions } from "../../core/formAnswer"; +const util = require('util'); describe("API data controller", () => { @@ -35,11 +39,7 @@ describe("API data controller", () => { request(server) .post("/answer/1") - .send({ - 1:["Answer to Question 1 Form 1"] - , 2:["12345-000"] - , 3:["MAXCHAR 10"] - }) + .send(formAnswerScenario.validAnswer) .expect(200) .expect((res: any) => { expect(res.body.id).to.be.equal(7); @@ -52,11 +52,7 @@ describe("API data controller", () => { request(server) .post("/answer/1") - .send({ - 1:["Answer to Question 1 Form 1"] - , 2:["12a345-000"] - , 3:["MAXCHAR 10 AND MORE"] - }) + .send(formAnswerScenario.invalidAnswer) .expect(500) .expect((res: any) => { const message = "Could not Create form Answer. Some error has occurred. Check error property for details."; @@ -73,11 +69,7 @@ describe("API data controller", () => { request(server) .post("/answer/10") - .send({ - 1:["Answer to Question 1 Form 1"] - , 2:["12a345-000"] - , 3:["MAXCHAR 10 AND MORE"] - }) + .send(formAnswerScenario.validAnswer) .expect(500) .expect((res: any) => { const message = "Form with id: '10' not found. Some error has occurred. Check error property for details."; @@ -87,4 +79,32 @@ describe("API data controller", () => { }) .end(done); }); + + it("Should respond 200 when reading a form answer", (done) => { + + request(server) + .get("/answer/1") + .set("Authorization", "bearer " + testToken) + .expect(200) + + .expect((res: any) => { + for (let i = 0; i < 3; ++i) { + TestHandler.testFormAnswer(res.body[i], formAnswerScenario.formAnswerRead[i]); + } + }) + .end(done); + }); + + it("Should respond 500 when failing to read a form answer", (done) => { + + request(server) + .get("/answer/500") + .set("Authorization", "bearer " + testToken) + .expect(500) + + .expect((res: any) => { + expect(res.body.error).to.be.equal("User dont own this form."); + }) + .end(done); + }); }); diff --git a/src/api/controllers/formAnswer.ts b/src/api/controllers/formAnswer.ts index 226068460d301a8519e42ded9deab3460bbfacd5..8234153255c1ae2c42093896c8008cc144275854 100644 --- a/src/api/controllers/formAnswer.ts +++ b/src/api/controllers/formAnswer.ts @@ -26,6 +26,8 @@ import { FormAnswer, FormAnswerOptions } from "../../core/formAnswer"; import { ValidationHandler } from "../../utils/validationHandler"; import { Response, NextFunction } from "express"; import { Request } from "../apiTypes"; +import { waterfall } from "async"; +const util = require('util'); export class AnswerCtrl { @@ -39,7 +41,7 @@ export class AnswerCtrl { }); return; } - + let inputAnswerOptionsDict: InputAnswerOptionsDict = {} for (const key of Object.keys(req.body)) { @@ -67,7 +69,7 @@ export class AnswerCtrl { if (err){ throw err; return; - } + } res.json({ id: formAnswerResult.id , message: "Answered" @@ -81,7 +83,7 @@ export class AnswerCtrl { , error: e.validationDict }); return; - } + } res.status(500).json({ message: "Could not Create form Answer. Some error has occurred. Check error property for details." @@ -91,4 +93,39 @@ export class AnswerCtrl { } }); } + + public static read(req: Request, res: Response, next: NextFunction) { + + waterfall([ + (callback: (err: Error) => void) => { + req.db.form.list(Object(req.userData).id, (err: Error, forms?: Form[]) => { + if (err) { + callback(err); + return; + } + + const e: Error = new Error("User dont own this form."); + callback((forms.some((obj) => obj.id === Number(req.params.id))) ? null : e); + }); + }, + (callback: (err: Error, answer?: FormAnswer[]) => void) => { + req.db.answer.readAll(req.params.id, (err: Error, resultAnswer?: FormAnswer[]) => { + if (err) { + callback(err); + return; + } + + res.status(200).json( resultAnswer ) + }); + } + ], (error: Error) => { + if (error) { + res.status(500).json({ + message: "Some error has ocurred. Check error property for details.", + error: error.message + }); + return; + } + }); + } } diff --git a/src/api/controllers/user.spec.ts b/src/api/controllers/user.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..b9d51024baf9f90bdca5d9514e940a3d663b0a8e --- /dev/null +++ b/src/api/controllers/user.spec.ts @@ -0,0 +1,320 @@ +/* + * form-creator-api. RESTful API to manage and answer forms. + * Copyright (C) 2019 Centro de Computacao Cientifica e Software Livre + * Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR + * + * This file is part of form-creator-api. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. +*/ + +import * as request from "supertest"; +import { expect } from "chai"; +import * as server from "../../main"; +import { testToken } from "./form.spec"; + +describe ("API data controller", () => { + + it ("Should respond 500 when failing to sign up a hash-null user", (done) => { + + request(server) + .post("/user/signUp") + .send({ + name: "Test_name" + , email: "test_email@test.com" + , hash: null + }) + .expect(500) + .expect((res: any) => { + expect(res.body).to.be.an("object"); + expect(res.body.message).to.be.an("string"); + expect(res.body.message).to.be.equal("Some error has ocurred. Check error property for details."); + expect(res.body.error).to.be.equal("data and salt arguments required"); + }) + .end(done); + }); + + it ("Should respond 500 when failing sign up a name-null user", (done) => { + + request(server) + .post("/user/signUp") + .send({ + email: "test_email@test.com" + , hash: "Test_pw" + }) + .expect(500) + .expect((res: any) => { + expect(res.body).to.be.an("object"); + expect(res.body.message).to.be.an("string"); + expect(res.body.message).to.be.equal("Some error has ocurred. Check error property for details."); + expect(res.body.error).to.be.equal("The dataType named 'name' was not found") + }) + .end(done); + }); + + it ("Should respond 500 when failing sign up an email-null user", (done) => { + + request(server) + .post("/user/signUp") + .send({ + name: "Test_name" + , hash: "Test_pw" + }) + .expect(500) + .expect((res: any) => { + expect(res.body).to.be.an("object"); + expect(res.body.message).to.be.an("string"); + expect(res.body.message).to.be.equal("Some error has ocurred. Check error property for details."); + expect(res.body.error).to.be.equal("The dataType named 'email' was not found"); + }) + .end(done); + }); + + it ("Should respond 500 when failing sign up an user with an existing email in the DB", (done) => { + + request(server) + .post("/user/signUp") + .send({ + name: "Test_name2" + , email: "test_email@test.com" + , hash: "Test_pw" + }) + .expect(500) + .expect((res: any) => { + expect(res.body).to.be.an("object"); + expect(res.body.message).to.be.an("string"); + expect(res.body.message).to.be.equal("Some error has ocurred. Check error property for details."); + expect(res.body.error).to.be.equal("Email exists on the database."); + }) + .end(done); + }); + + it("Should respond 500 when failing to validate an user by wrong password", (done) => { + + request(server) + .post("/user/signIn") + .send({ + email: "test_email@test.com" + , hash: "Test_p" + }) + .expect(500) + .expect((res: any) => { + expect(res.body).to.be.an("object"); + expect(res.body.message).to.be.an("string"); + expect(res.body.message).to.be.equal("Authentication fail."); + }) + .end(done); + }); + + it("Should respond 500 when failing to validate an user by email not in the DB", (done) => { + + request(server) + .post("/user/signIn") + .send({ + email: "test_Unexisting_email@test.com" + , hash: "Test_pw" + }) + .expect(500) + .expect((res: any) => { + expect(res.body).to.be.an("object"); + expect(res.body.message).to.be.an("string"); + expect(res.body.message).to.be.equal("Authentication fail."); + }) + .end(done); + }); + + it("Should respond 500 when failing to validate an email-null user", (done) => { + + request(server) + .post("/user/signIn") + .send({ + hash: "Test_pw" + }) + .expect(500) + .expect((res: any) => { + expect(res.body).to.be.an("object"); + expect(res.body.message).to.be.an("string"); + expect(res.body.message).to.be.equal("Authentication fail."); + }) + .end(done); + }); + + it("Should respond 500 when failing to validate an hash-null user", (done) => { + + request(server) + .post("/user/signIn") + .send({ + email: "test_email@test.com" + }) + .expect(500) + .expect((res: any) => { + expect(res.body).to.be.an("object"); + expect(res.body.message).to.be.an("string"); + expect(res.body.message).to.be.equal("Authentication fail."); + }) + .end(done); + }); + + it("Should respond 200 when changing an user's password", (done) => { + + request(server) + .put("/user/changePassword") + .set("Authorization", "bearer " + testToken) + .send({ + hash: "changed_pw_hashing" + }) + .expect(200) + + .expect((res: any) => { + expect(res.body).to.be.an("object"); + expect(res.body.message).to.be.an("string"); + expect(res.body.message).to.be.eql("Password changed with sucess."); + }) + .end(done); + }); + + it("Should respond 200 when listing forms from an user", (done) => { + + request(server) + .get("/user/list/1") + .expect(200) + .expect((res: any) => { + expect(res.body).to.be.an("array"); + let j: number = 1; + for (const i of res.body) { + expect(i.id).to.be.eql(j++); + } + }) + .end(done); + }); + + it("Should respond 200 when updating an user info", (done) => { + + request(server) + .put("/user/update") + .send({ + name: "test_name_update" + , email: "test_email@test.com" + }) + .set("Authorization", "bearer " + testToken) + .expect(200) + .expect((res: any) => { + expect(res.body).to.be.an("object"); + expect(res.body.message).to.be.an("string"); + expect(res.body.message).to.be.equal("Updated"); + }) + .end(done); + }); + + it("Should respond 500 when failing on updating an user info by email missing", (done) => { + + request(server) + .put("/user/update") + .send({ + name: "test_name_update" + }) + .set("Authorization", "bearer " + testToken) + .expect(500) + .expect((res: any) => { + expect(res.body).to.be.an("object"); + expect(res.body.message).to.be.an("string"); + expect(res.body.message).to.be.equal("Invalid User. Check error property for details."); + expect(res.body.error).to.be.equal("The dataType named 'email' was not found"); + }) + .end(done); + }); + + it("Should respond 500 when failing on updating an user info by email missing", (done) => { + + request(server) + .put("/user/update") + .send({ + email: "test_email@test.com" + }) + .set("Authorization", "bearer " + testToken) + .expect(500) + .expect((res: any) => { + expect(res.body).to.be.an("object"); + expect(res.body.message).to.be.an("string"); + expect(res.body.message).to.be.equal("Invalid User. Check error property for details."); + expect(res.body.error).to.be.equal("The dataType named 'name' was not found"); + }) + .end(done); + }); + + it("Should respond 200 when deleting an user from the database", (done) => { + + request(server) + .delete("/user/deleteData/1") + .set("Authorization", "bearer " + testToken) + .expect(200) + + .expect((res: any) => { + expect(res.body).to.be.an("object"); + expect(res.body.message).to.be.an("string"); + expect(res.body.message).to.be.equal("User data deleted."); + }) + .end(done); + }); + + it("Should respond 500 when failing to delete an user from the database", (done) => { + + request(server) + .delete("/user/deleteData/1") + .set("Authorization", "bearer " + testToken) + .expect(500) + + .expect((res: any) => { + expect(res.body).to.be.an("object"); + expect(res.body.message).to.be.an("string"); + expect(res.body.message).to.be.equal("Failed to delete user. Check error properties for details."); + expect(res.body.error).to.be.eql("Invalid ID, user doesn't exist."); + }) + .end(done); + }); + + it("Should respond 500 when failing to delete an user by incompatible ID", (done) => { + + request(server) + .delete("/user/deleteData/2") + .set("Authorization", "bearer " + testToken) + .expect(500) + + .expect((res: any) => { + expect(res.body).to.be.an("object"); + expect(res.body.message).to.be.an("string"); + expect(res.body.message).to.be.equal("Unauthorized action."); + }) + .end(done); + }); + + it("Should respond 500 when failing to change a password", (done) => { + + request(server) + .put("/user/changePassword") + .set("Authorization", "bearer "+ testToken) + .send({ + hash: "changed_pw_hashing" + }) + .expect(500) + + .expect((res: any) => { + expect(res.body).to.be.an("object"); + expect(res.body.message).to.be.an("string"); + expect(res.body.message).to.be.eql("Some error has ocurred. Check error property for details.") + expect(res.body.error).to.be.eql("Bad amount of ids returned: found '0' should be 1"); + }) + .end(done); + }); +}); diff --git a/src/api/controllers/user.ts b/src/api/controllers/user.ts new file mode 100644 index 0000000000000000000000000000000000000000..7650132da62cf0195e2b927c7920e04248050e17 --- /dev/null +++ b/src/api/controllers/user.ts @@ -0,0 +1,281 @@ +/* + * form-creator-api. RESTful API to manage and answer forms. + * Copyright (C) 2019 Centro de Computacao Cientifica e Software Livre + * Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR + * + * This file is part of form-creator-api. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +import { User } from "../../core/user"; +import { Response, NextFunction } from "express"; +import { Request } from "../apiTypes"; +import { OptHandler } from "../../utils/optHandler"; +import { waterfall } from "async"; +import * as bcrypt from "bcrypt"; +import * as jwt from "jsonwebtoken"; +import { Form } from "../../core/form"; + +export class UserCtrl { + + public static signUp(req: Request, res: Response, next: NextFunction) { + + let newUser: User; + + waterfall ([ + (callback: (err: Error, user?: User) => void) => { + bcrypt.hash(req.body.hash, 10, (err: Error, hashedPw: string) => { + if (err) { + callback(err); + return; + } + try { + newUser = new User(OptHandler.User(req.body, hashedPw)); + } catch (err) { + callback(err); + return; + } + callback(null, newUser); + }); + }, + (user: User, callback: (err: Error) => void) => { + req.db.user.write(user, (err: Error) => { + if (err) { + callback(err); + return; + } else { + res.json({ + message: "User registered with sucess." + }); + callback(null); + return; + } + }); + } + + ], (error: Error) => { + if (error) { + res.status(500).json({ + message: "Some error has ocurred. Check error property for details.", + error: error.message + }); + return; + } + }); + } + + public static signIn(req: Request, res: Response, next: NextFunction) { + + waterfall([ + (callback: (err: Error, userId?: number) => void) => { + req.db.user.executeVerifyEmail(req.body.email, (err: Error, id?: number) => { + if (id !== undefined) { + callback(null, id); + return; + } + + if (err) { + callback(err); + return; + } + + callback(new Error("Auth Failed")); + }); + }, + (id: number, callback: (err: Error, user?: User) => void) => { + req.db.user.read(id, (err: Error, user?: User) => { + if (err) { + callback(err); + return; + } + callback(null, user); + }); + }, + (user: User, callback: (err: Error, user?: User) => void ) => { + bcrypt.compare(req.body.hash, user.hash, (err: Error, result?: boolean) => { + if (err !== undefined) { + callback(err); + return; + } + if (!result) { + callback(new Error("Auth failed")); + return; + } + callback(null, user); + }); + }, + (user: User, callback: (err: Error) => void ) => { + jwt.sign( + { + email: user.email + , id: user.id + }, + process.env.JWT_KEY + ,{ + expiresIn: "1h" + }, + (err: Error, encoded: string) => { + if (err) { + callback(err); + return; + } + res.json({ + message: "Authentication successful.", + token: encoded + }); + callback(null); + }) + } + ], (err: Error) => { + if (err) { + res.status(500).json({ + message: "Authentication fail." + }); + return; + } + return; + }); + } + + public static deleteData (req: Request, res: Response, next: NextFunction) { + + if (Object(req.userData).id !== Number(req.params.id)) { + res.status(500).json({ + message: "Unauthorized action." + }) + return; + } + + req.db.user.delete(Object(req.userData).id, (err: Error) => { + + if (err) { + res.status(500).json({ + message: "Failed to delete user. Check error properties for details." + , error: err.message + }); + return; + } + + res.status(200).json({ + message: "User data deleted." + }); + }); + } + + public static changePassword (req: Request, res: Response, next: NextFunction) { + + let newUser: User; + + waterfall ([ + (callback: (err: Error, password?: string) => void) => { + bcrypt.hash(req.body.hash, 10, (err: Error, hashedPw: string) => { + if (err) { + callback(err); + return; + } + + callback(null, hashedPw); + }); + }, + (password: string, callback: (err: Error, user?: User) => void) => { + req.db.user.read(Object(req.userData).id, (err: Error, user?: User) => { + + if (err) { + callback(err); + return; + } + + newUser = new User(OptHandler.User(user, password)); + + callback(null, newUser); + }); + }, + (user: User, callback: (err: Error) => void) => { + req.db.user.update(user, Object(req.userData).id, (err: Error) => { + if (err) { + callback(err); + return; + } else { + res.json({ + message: "Password changed with sucess." + }); + callback(null); + return; + } + }); + } + + ], (error: Error) => { + if (error) { + res.status(500).json({ + message: "Some error has ocurred. Check error property for details.", + error: error.message + }); + return; + } + }); + } + + public static listForms (req: Request, res: Response, next: NextFunction) { + + req.db.form.list(req.params.id, (err: Error, forms?: Form[]) => { + if (err){ + res.status(500).json({ + message: "Could not list forms. Some error has occurred. Check error property for details.", + error: err + }); + return; + } + + const mappedForms = forms.map(form => ({ + id: form.id + , title: form.title + , description: form.description + })); + + res.json(mappedForms); + return; + }); + } + + public static update (req: Request, res: Response, next: NextFunction) { + + let newUser: User; + try { + newUser = new User(OptHandler.User(req.body)); + } catch(e) { + res.status(500).json({ + message: "Invalid User. Check error property for details." + , error: e.message + }); + return; + } + waterfall([ + (callback: (err: Error, userResult?: User) => void) => { + req.db.user.update(newUser, Object(req.userData).id, callback); + } + ], (err) => { + if (err) { + res.status(500).json({ + message: "Could not update Form. Some error has ocurred. Check error property for details." + , error: err.message + }); + return; + } + res.json({ message: "Updated" }); + return; + }); + } +} \ No newline at end of file diff --git a/src/api/middlewares/userAuth.ts b/src/api/middlewares/userAuth.ts new file mode 100644 index 0000000000000000000000000000000000000000..511d79243e0331e190d4eeb28e6dc0e64651128c --- /dev/null +++ b/src/api/middlewares/userAuth.ts @@ -0,0 +1,38 @@ +/* + * form-creator-api. RESTful API to manage and answer forms. + * Copyright (C) 2019 Centro de Computacao Cientifica e Software Livre + * Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR + * + * This file is part of form-creator-api. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +import * as jwt from "jsonwebtoken"; +import { Middleware } from "../apiTypes"; + +export function tokenValidation (): Middleware { + + return function (req, res, next) { + try { + req.userData = jwt.verify(req.headers.authorization.split(" ")[1], process.env.JWT_KEY); + next(); + } catch (error) { + res.status(401).json ({ + message: "Unauthorized request" + }); + return; + } + } +}; \ No newline at end of file diff --git a/src/core/formAnswer.ts b/src/core/formAnswer.ts index 04f2ba671d2a879212bdc36bda1e519740c0850d..85ca12d7906f79c261f5cc835b5b27a7293e7840 100644 --- a/src/core/formAnswer.ts +++ b/src/core/formAnswer.ts @@ -60,7 +60,7 @@ export class FormAnswer { this.form = options.form; this.timestamp = options.timestamp; this.inputAnswers = {}; - for (const key of Object.keys(options.inputsAnswerOptions)){ + for (const key of Object.keys(options.inputsAnswerOptions)) { this.inputAnswers[parseInt(key, 10)] = options.inputsAnswerOptions[parseInt(key, 10)].map( (i: InputAnswerOptions) => { return new InputAnswer(i); }); diff --git a/src/core/formUpdate.spec.ts b/src/core/formUpdate.spec.ts index 7c54fe5df5900adac41751690faf6c95274c0dd3..d7c0aa791672e6f1de15ce99517591d00fab32e0 100644 --- a/src/core/formUpdate.spec.ts +++ b/src/core/formUpdate.spec.ts @@ -20,6 +20,7 @@ */ import { expect } from "chai"; +import { formUpdateScenario } from "../../test/scenario"; import { InputType, UpdateType, ValidationType } from "../utils/enumHandler"; import { TestHandler } from "../utils/testHandler"; import { Form, FormOptions } from "./form"; @@ -29,64 +30,7 @@ import { InputUpdate, InputUpdateOptions } from "./inputUpdate"; describe("FormUpdate", () => { it("should create a valid FormUpdate with id null", (done) => { - - const inputObj: InputOptions = { - placement: 0 - , description: "Description Question 1 Form 1" - , question: "Question 1 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [] - , id: 1 - }; - const formObj: FormOptions = { - id: 1 - , title: "Form Title 1" - , description: "Form Description 1" - , inputs: [ inputObj ] - }; - const inputUpdateObj: InputUpdateOptions = { - input: inputObj - , inputOperation: UpdateType.ADD - , value: null - }; - const formUpdateObj: FormUpdateOptions = { - form: formObj - , updateDate: new Date() - , inputUpdates: [ inputUpdateObj ] - }; - - const resFormUpdate: FormUpdate = new FormUpdate(formUpdateObj); - - const expInput: Input = { - placement: 0 - , description: "Description Question 1 Form 1" - , question: "Question 1 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [] - , id: 1 - }; - const expForm: Form = { - id: 1 - , title: "Form Title 1" - , description: "Form Description 1" - , inputs: [ expInput ] - }; - const expInputUpdate: InputUpdate = { - input: expInput - , inputOperation: UpdateType.ADD - , value: null - , id: null - }; - const expFormUpdate: FormUpdate = { - form: expForm - , updateDate: new Date() - , inputUpdates: [ expInputUpdate] - , id: null - }; - - TestHandler.testFormUpdate(resFormUpdate, expFormUpdate); + TestHandler.testFormUpdate(formUpdateScenario.resFormUpdateIdNull, formUpdateScenario.expFormUpdate); done(); }); }); diff --git a/src/core/input.spec.ts b/src/core/input.spec.ts index 58df25055ad292cc0d05412c00283f102bc02c63..7b2c5b1e0800612a0df34c9e496dd2fb3f06d369 100644 --- a/src/core/input.spec.ts +++ b/src/core/input.spec.ts @@ -20,78 +20,36 @@ */ import { expect } from "chai"; +import { inputScenario } from "../../test/scenario"; import { InputType, ValidationType } from "../utils/enumHandler"; import { Input, InputOptions } from "./input"; describe("Input with enabled atribute", () => { it("should create a Input with option key 'enabled' as undefined", (done) => { - const options: InputOptions = { - placement: 0 - , description: "Description Question 1" - , question: "Question 1" - , type: InputType.TEXT - , validation: [] - }; - - const input: Input = new Input(options); - - expect(input).to.be.a("object"); - expect(input.enabled).to.be.equal(true); - + expect(inputScenario.inputUndefEnable).to.be.a("object"); + expect(inputScenario.inputUndefEnable.enabled).to.be.equal(true); done(); }); it("should create a Input with option key 'enabled' as null", (done) => { - const options: InputOptions = { - placement: 1 - , description: "Description Question 2" - , question: "Question 2" - , enabled: null - , type: InputType.TEXT - , validation: [] - }; - - const input: Input = new Input(options); - - expect(input).to.be.a("object"); - expect(input.enabled).to.be.equal(true); + expect(inputScenario.inputNullEnable).to.be.a("object"); + expect(inputScenario.inputNullEnable.enabled).to.be.equal(true); done(); }); it("should create a Input with option key 'enabled' as true", (done) => { - const options: InputOptions = { - placement: 1 - , description: "Description Question 2" - , question: "Question 2" - , enabled: true - , type: InputType.TEXT - , validation: [] - }; - - const input: Input = new Input(options); - - expect(input).to.be.a("object"); - expect(input.enabled).to.be.equal(true); + expect(inputScenario.inputTrueEnable).to.be.a("object"); + expect(inputScenario.inputTrueEnable.enabled).to.be.equal(true); done(); }); it("should create a Input with option key 'enabled' as false", (done) => { - const options: InputOptions = { - placement: 1 - , description: "Description Question 2" - , question: "Question 2" - , enabled: false - , type: InputType.TEXT - , validation: [] - }; - - const input: Input = new Input(options); - expect(input).to.be.a("object"); - expect(input.enabled).to.be.equal(false); + expect(inputScenario.inputFalseEnable).to.be.a("object"); + expect(inputScenario.inputFalseEnable.enabled).to.be.equal(false); done(); }); diff --git a/src/core/input.ts b/src/core/input.ts index c199c378fe37c9c3d11af2780f71fd861b3de3b1..59d161c4e537b12442817f6d2edc5359ec19e00b 100644 --- a/src/core/input.ts +++ b/src/core/input.ts @@ -20,6 +20,7 @@ */ import { InputType, ValidationType } from "../utils/enumHandler"; +import { SubForm, SubFormOptions } from "./subForm"; /** Parameters used to create a input object. */ export interface InputOptions { @@ -39,6 +40,8 @@ export interface InputOptions { validation: Validation[]; /** Question answer sugestion */ sugestions?: Sugestion[]; + /** A SubForm object that is a reference to another Form */ + subForm?: SubFormOptions; } /** Validation contains the type of it, and n arguments to validate if necessary */ export interface Validation { @@ -76,8 +79,9 @@ export class Input { /** Array contain all input's validation */ public readonly validation: Validation[]; /** Question answer sugestions */ - public readonly sugestions?: Sugestion[]; - + public readonly sugestions: Sugestion[]; + /** A SubForm object that is a reference to another Form */ + public readonly subForm: SubForm; /** * Creates a new instance of Input Class * @param options - InputOptions instance to create a input. @@ -96,10 +100,14 @@ export class Input { this.type = options.type; if (options.sugestions) { this.sugestions = options.sugestions.map((i: any) => i); - } - else { + } else { this.sugestions = []; } + if (options.subForm) { + this.subForm = new SubForm(options.subForm); + } else { + this.subForm = null; + } this.validation = options.validation; } } diff --git a/src/core/inputAnswer.ts b/src/core/inputAnswer.ts index 6737a5cb0c1e14c6dce27a2195c129e906310f4a..c706e3eba8cf501d1761a4e61dfed3e9a356b4fa 100644 --- a/src/core/inputAnswer.ts +++ b/src/core/inputAnswer.ts @@ -19,42 +19,49 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ - /** Parameters used to create a input object. */ - export interface InputAnswerOptions { - /** Unique identifier of a InputAnswer instance. */ - id?: number; - /** Input id which this answer came from. */ - idInput: number; - /** Place where answers should be (multivalored answers). */ - placement: number; - /** Input's answer */ - value: string; - } +import { OptHandler } from "../utils/optHandler"; +import { FormAnswer, FormAnswerOptions } from "./formAnswer"; + +/** Parameters used to create a input object. */ +export interface InputAnswerOptions { + /** Unique identifier of a InputAnswer instance. */ + id?: number; + /** Input id which this answer came from. */ + idInput: number; + /** Place where answers should be (multivalored answers). */ + placement: number; + /** Input's answer */ + value: string; + /** SubForm's answer */ + subForm?: FormAnswerOptions; +} /** Parameters used to create a dictionary to uses as an collection of InputAnswerOptions Object. */ - export interface InputAnswerOptionsDict { - /** A key is a identifier of a Input instance */ +export interface InputAnswerOptionsDict { + /** A key is a identifier of a Input instance */ [key: number]: InputAnswerOptions[]; } /** Parameters used to create a dictionary to uses as an collection of InputAnswer Object.. */ - export interface InputAnswerDict { - /** A key is a identifier of a Input instance */ +export interface InputAnswerDict { + /** A key is a identifier of a Input instance */ [key: number]: InputAnswer[]; } /** * Input Class to manage project's inputs forms */ - export class InputAnswer { +export class InputAnswer { /** Unique identifier of a Input Answer instance. */ public readonly id: number; /** A identifier of a Input instance. */ public readonly idInput: number; /** Place where input should be in the form. */ public readonly placement: number; - /** Input's Description */ + /** Input's Description. */ public readonly value: string; + /** SubForm Answer. */ + public readonly subForm: FormAnswer; /** * Creates a new instance of InputAnswer Class @@ -65,6 +72,7 @@ this.idInput = options.idInput; this.placement = options.placement; this.value = options.value; + this.subForm = options.subForm ? new FormAnswer(OptHandler.formAnswer(options.subForm)) : null; } } diff --git a/src/core/inputUpdate.spec.ts b/src/core/inputUpdate.spec.ts index 3e6b91247229ee6af78a099bbdde620154ef8875..dc7fe8dd6264bdd58698c77ffb59908c0b4d515e 100644 --- a/src/core/inputUpdate.spec.ts +++ b/src/core/inputUpdate.spec.ts @@ -20,6 +20,7 @@ */ import { expect } from "chai"; +import { inputUpdateScenario } from "../../test/scenario"; import { InputType, UpdateType, ValidationType } from "../utils/enumHandler"; import { TestHandler } from "../utils/testHandler"; import { Input, InputOptions } from "./input"; @@ -27,41 +28,7 @@ import { InputUpdate, InputUpdateOptions } from "./inputUpdate"; describe("InputUpdate", () => { it("should create a valid InputUpdate with id null", (done) => { - - const inputObj: InputOptions = { - placement: 0 - , description: "Description Question 1 Form 1" - , question: "Question 1 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [] - , id: 1 - }; - const inputUpdateObj: InputUpdateOptions = { - input: inputObj - , inputOperation: UpdateType.ADD - , value: null - }; - - const resInputUpdate: InputUpdate = new InputUpdate(inputUpdateObj); - - const expInput: Input = { - placement: 0 - , description: "Description Question 1 Form 1" - , question: "Question 1 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [] - , id: 1 - }; - const expInputUpdate: InputUpdate = { - input: expInput - , inputOperation: UpdateType.ADD - , value: null - , id: null - }; - - TestHandler.testInputUpdate(resInputUpdate, expInputUpdate); + TestHandler.testInputUpdate(inputUpdateScenario.resInputUpdate, inputUpdateScenario.expInputUpdate); done(); }); }); diff --git a/src/core/subForm.spec.ts b/src/core/subForm.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..e70e86c32c425444b514a039a87452919a093d21 --- /dev/null +++ b/src/core/subForm.spec.ts @@ -0,0 +1,20 @@ +/* + * form-creator-api. RESTful API to manage and answer forms. + * Copyright (C) 2019 Centro de Computacao Cientifica e Software Livre + * Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR + * + * This file is part of form-creator-api. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ diff --git a/src/core/subForm.ts b/src/core/subForm.ts new file mode 100644 index 0000000000000000000000000000000000000000..2482576971356458e8040a8594831a1fea38cb1c --- /dev/null +++ b/src/core/subForm.ts @@ -0,0 +1,58 @@ +/* + * form-creator-api. RESTful API to manage and answer forms. + * Copyright (C) 2019 Centro de Computacao Cientifica e Software Livre + * Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR + * + * This file is part of form-creator-api. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +import { OptHandler } from "../utils/optHandler"; +import { Form, FormOptions } from "./form"; + +/** Parameters used to create a subform object. */ +export interface SubFormOptions { + /** Unique identifier of a SubForm instance */ + id?: number; + + /** Input wich subForm is linked. */ + inputId: number; + + /** SubForm of a form. */ + contentFormId: number; +} +/** + * Form Class to manage project's forms + */ +export class SubForm { + /** Unique identifier of a SubForm instance */ + public readonly id: number; + + /** Input wich subForm is linked. */ + public readonly inputId: number; + + /** SubForm of a form. */ + public readonly contentFormId: number; + + /* + * Creates a new instance of Form Class + * @param options - FormOptions instance to create a form. + */ + constructor(options: SubFormOptions) { + this.id = options.id ? options.id : null; + this.inputId = options.inputId; + this.contentFormId = options.contentFormId; + } +} diff --git a/src/core/user.ts b/src/core/user.ts new file mode 100644 index 0000000000000000000000000000000000000000..e5f39b4f1175c3e519b457dab8b0fc08f166cdce --- /dev/null +++ b/src/core/user.ts @@ -0,0 +1,70 @@ +/* + * form-creator-api. RESTful API to manage and answer forms. + * Copyright (C) 2019 Centro de Computacao Cientifica e Software Livre + * Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR + * + * This file is part of form-creator-api. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +/** Parameters used to create an User object. */ +export interface UserOptions { + /** Unique identifier of an User instance. */ + id?: number; + /** Unique User e-mail. */ + email: string; + /** User's name. */ + name: string; + /** Unique User's hash. */ + hash?: string; + /** Determine either an user is electable or not */ + enabled?: boolean; + /** User forms */ + forms?: number[]; +} + +/** + * Export class to manage project's User. + */ +export class User { + /** Unique identifier of an User instance. */ + public readonly id: number; + /** Unique User e-mail. */ + public readonly email: string; + /** User's name. */ + public readonly name: string; + /** Unique User's hash. */ + public readonly hash: string; + /** Determine either an user is electable or not */ + public readonly enabled: boolean; + /** User forms */ + public readonly forms: number[]; + + /** + * Creates a new instance of User class. + * @param options - Inputs to create an User. + */ + constructor(options: UserOptions) { + this.id = options.id ? options.id : null; + this.email = options.email; + this.name = options.name; + this.hash = options.hash; + this.enabled = options.enabled; + if ((options.enabled === undefined) || (options.enabled === null) ) { + this.enabled = true; + } + this.forms = options.forms ? options.forms : null; + } +} diff --git a/src/main.ts b/src/main.ts index 110396cbee8c8c194ccfde88f431705abdc2ca28..70d6a76d84b69b58af5558346dc9d2edf91c2075 100755 --- a/src/main.ts +++ b/src/main.ts @@ -41,6 +41,8 @@ import { DbHandlerMw } from "./api/middlewares/dbHandler"; // Include controllers import { FormCtrl } from "./api/controllers/form"; import { AnswerCtrl } from "./api/controllers/formAnswer"; +import { UserCtrl } from "./api/controllers/user" +import { tokenValidation } from "./api/middlewares/userAuth"; // Setup middlewares app.use("/", bodyParser.json()); @@ -48,11 +50,17 @@ app.use("/", DbHandlerMw()); // Setup routes -app.get("/form/", FormCtrl.list); app.get("/form/:id", FormCtrl.read); -app.put("/form/:id", FormCtrl.update); -app.post("/form", FormCtrl.write); +app.put("/form/:id", tokenValidation(), FormCtrl.update); +app.post("/form", tokenValidation(), FormCtrl.write); app.post("/answer/:id", AnswerCtrl.write); +app.post("/user/signUp", UserCtrl.signUp); +app.post("/user/signIn", UserCtrl.signIn); +app.delete("/user/deleteData/:id", tokenValidation(), UserCtrl.deleteData); +app.put("/user/changePassword", tokenValidation(), UserCtrl.changePassword); +app.get("/user/list/:id", UserCtrl.listForms); +app.put("/user/update", tokenValidation(), UserCtrl.update); +app.get("/answer/:id", tokenValidation(), AnswerCtrl.read); // Listening diff --git a/src/utils/answerQueryBuilder.ts b/src/utils/answerQueryBuilder.ts index 8363e93f4723ba35cdabcd37e51fcb2d985881ce..56e7a8f26af5458ddf0eec51dd33472cbb75eb98 100644 --- a/src/utils/answerQueryBuilder.ts +++ b/src/utils/answerQueryBuilder.ts @@ -19,13 +19,11 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ -import { eachSeries, map, waterfall } from "async"; -import { Pool, PoolConfig, QueryResult } from "pg"; -import { Form, FormOptions } from "../core/form"; +import { eachSeries, waterfall } from "async"; +import { Pool, QueryResult } from "pg"; +import { Form } from "../core/form"; import { FormAnswer, FormAnswerOptions } from "../core/formAnswer"; -import { Input, InputOptions } from "../core/input"; -import { InputAnswer, InputAnswerDict, InputAnswerOptions, InputAnswerOptionsDict } from "../core/inputAnswer"; -import { EnumHandler, InputType } from "./enumHandler"; +import { InputAnswer, InputAnswerOptions, InputAnswerOptionsDict } from "../core/inputAnswer"; import { ErrorHandler} from "./errorHandler"; import { FormQueryBuilder } from "./formQueryBuilder"; import { OptHandler } from "./optHandler"; @@ -55,19 +53,17 @@ export class AnswerQueryBuilder extends QueryBuilder { waterfall([ (callback: (err: Error, result?: QueryResult) => void) => { - this.begin((error: Error, results?: QueryResult) => { + this.begin((error: Error) => { callback(error); }); }, - (callback: (err: Error, result?: number) => void) => { this.writeController(formAnswer, (error: Error, resultAnswerId?: number) => { callback(error, resultAnswerId); }); }, - (formAnswerId: number, callback: (err: Error, result?: number) => void) => { - this.commit((error: Error, results?: QueryResult) => { + this.commit((error: Error) => { callback(error, formAnswerId); }); }, @@ -79,7 +75,7 @@ export class AnswerQueryBuilder extends QueryBuilder { ], (err, formAnswerResult?: FormAnswer) => { if (err) { - this.rollback((error: Error, results?: QueryResult) => { + this.rollback(() => { cb(err); }); return; @@ -109,19 +105,73 @@ export class AnswerQueryBuilder extends QueryBuilder { (formAnswerId: number, callback: (err: Error, result?: any) => void) => { eachSeries(Object.keys(formAnswer.inputAnswers), (key, outerCallback) => { eachSeries(formAnswer.inputAnswers[parseInt(key, 10)], (inputsAnswer, innerCallback) => { - this.executeWriteInput(formAnswerId, inputsAnswer, innerCallback); + this.writeInputController(formAnswerId, inputsAnswer, innerCallback); }, (error) => { outerCallback(error); }); - }, (err, id?: number) => { + }, (err) => { callback(err, formAnswerId); }); } ], (err, id?: number) => { cb(err, id); + return; + }); + } + + /** + * Asynchronously write a Answer on database without transactions. + * @param formAnswerId - FormAnswer identifier to link with inputAnswer. + * @param inputAnswer - InputAnswer to be inserted. + * @param cb - Callback function which contains the data read. + * @param cb.err - Error information when the method fails. + */ + private writeInputController(formAnswerId: number, inputAnswer: InputAnswer, cb: (err: Error) => void) { + waterfall([ + (callback: (err: Error, result?: number) => void) => { + this.executeWriteInput(formAnswerId, inputAnswer, (err: Error, id?: number) => { + callback(err, id); + }); + }, + (inputId: number, callback: (err: Error) => void) => { + this.writeSubFormController(inputAnswer.subForm, inputId, (err: Error) => { + callback(err); + }); + } + ], () => { + cb(null); + return; }); } + /** + * Asynchronously write a Answer on database without transactions. + * @param subForm - FormAnswer that will be inserted. + * @param inputId - InputAnswer identifier to be linked with the subFormAnswer (FormAnswer object). + * @param cb - Callback function which contains the data read. + * @param cb.err - Error information when the method fails. + */ + private writeSubFormController(subForm: FormAnswer, inputId: number, cb: (err: Error) => void) { + if (subForm !== null) { + waterfall([ + (callback: (err: Error, result?: number) => void) => { + this.writeController(subForm, (err: Error, subFormId?: number) => { + callback(err, subFormId); + }); + }, + (subFormId: number, callback: (err: Error) => void) => { + this.executeUpdateInput(subFormId, inputId, callback); + } + ], (err) => { + cb(err); + return; + }); + } else { + cb(null); + return; + } + } + /** * Asynchronously insert a FormAnswer on database. * @param formAnswer - FormAnswer to be inserted. @@ -156,77 +206,116 @@ export class AnswerQueryBuilder extends QueryBuilder { } /** - * Asynchronously insert a inputAnswer on database. - * @param formAnswerId - Indentifier to relate with InputAnswer. - * @param cb - Callback function. + * Asynchronously write a Answer on database without transactions. + * @param formAnswer - FormAnswer identifier to be linked with the inputAnswer. + * @param inputAnswer - InputAnswer that should be inserted. + * @param cb - Callback function which contains the data read. * @param cb.err - Error information when the method fails. + * @param cb.result - InputAnswer identifier or null if any error occurs. */ - private executeWriteInput(formAnswerId: number, inputAnswer: InputAnswer, cb: (err: Error) => void) { - const queryString: string = "INSERT INTO input_answer (id_form_answer, id_input, value, placement) \ - VALUES ( $1, $2, $3, $4) \ + private executeWriteInput(formAnswerId: number, inputAnswer: InputAnswer, cb: (err: Error, result?: number) => void) { + const queryString: string = "INSERT INTO input_answer (id_form_answer, id_input, id_sub_form, value, placement) \ + VALUES ( $1, $2, $3, $4, $5 ) \ RETURNING id;"; const query: QueryOptions = { query: queryString , parameters: [ formAnswerId , inputAnswer.idInput + , inputAnswer.subForm ? inputAnswer.subForm.id : null , inputAnswer.value , inputAnswer.placement ] }; this.executeQuery(query, (err: Error, result?: QueryResult) => { - if (err){ + + if (err) { cb(err); return; } - if (result.rowCount !== 1){ + if (result.rowCount !== 1) { cb(ErrorHandler.notInserted("InputsAnswer")); return; } - cb(null); + + cb(null, result.rows[0]["id"]); }); } /** - * Asynchronously read a FormAnswer from database. - * @param formAnswerId - FormAnswer identifier to be founded. - * @param cb - Callback function which contains the data read. + * Asynchronously insert a inputAnswer on database without transactions. + * @param formAnswerId - Indentifier to relate with InputAnswer. + * @param inputAnswerId - InputAnswer Indentifier to be updated. + * @param cb - Callback function. * @param cb.err - Error information when the method fails. - * @param cb.formAnswers - FormAnswer object or null if form not exists. */ + private executeUpdateInput(formAnswerId: number, inputAnswerId: number, cb: (err: Error) => void) { + const queryString: string = "UPDATE input_answer \ + SET id_sub_form=$1 \ + WHERE id=$2;"; + const query: QueryOptions = { + query: queryString + , parameters: [ + formAnswerId + , inputAnswerId + ] + }; + + this.executeQuery(query, (err: Error, result?: QueryResult) => { + if (err){ + cb(err); + return; + } + + if (result.rowCount !== 1){ + cb(ErrorHandler.notInserted("InputsAnswer")); + return; + } + + cb(null); + }); + } + + /** + * Asynchronously read a FormAnswer from database. + * @param formAnswerId - FormAnswer identifier to be founded. + * @param cb - Callback function which contains the data read. + * @param cb.err - Error information when the method fails. + * @param cb.formAnswers - FormAnswer object or null if form not exists. + */ public read(formAnswerId: number, cb: (err: Error, formAnswers?: FormAnswer) => void) { waterfall([ (callback: (err: Error, result?: QueryResult) => void) => { - this.begin((error: Error, results?: QueryResult) => { + this.begin((error: Error) => { callback(error); }); }, - (callback: (err: Error, result?: FormAnswer) => void) => { - this.readController(formAnswerId, (error: Error, resultAnswer?: FormAnswer) => { - callback(error, resultAnswer); + (callback: (err: Error, result?: FormAnswerOptions) => void) => { + this.readController(formAnswerId, (error: Error, result?: FormAnswerOptions) => { + callback(error, result ? result : null); }); }, - (formAnswer: FormAnswer, callback: (err: Error, result?: FormAnswer) => void) => { - this.commit((error: Error, results?: QueryResult) => { + (formAnswer: FormAnswerOptions, callback: (err: Error, result?: FormAnswerOptions) => void) => { + this.commit((error: Error) => { callback(error, formAnswer); }); }, - (formAnswer: FormAnswer, callback: (err: Error, result?: FormAnswer) => void) => { + (formAnswer: FormAnswerOptions, callback: (err: Error, result?: FormAnswer) => void) => { this.formQueryBuilder.read(formAnswer.form.id, (error: Error, resultForm?: Form) => { - const formAnswerObj: FormAnswer = { + const formAnswerObj: FormAnswerOptions = { id: formAnswer.id , form: resultForm , timestamp: formAnswer.timestamp - , inputAnswers: formAnswer.inputAnswers + , inputsAnswerOptions: formAnswer.inputsAnswerOptions }; - callback(error, formAnswerObj); + callback(error, new FormAnswer(formAnswerObj)); }); } ], (err, formAnswer?: FormAnswer) => { if (err) { - this.rollback((error: Error, results?: QueryResult) => { + this.rollback(() => { cb(err); }); return; @@ -235,33 +324,131 @@ export class AnswerQueryBuilder extends QueryBuilder { }); } + /** + * Asynchronously read all FormAnswer from a form database with transaction. + * @param formId - Form identifier to be founded. + * @param cb - Callback function which contains the data read. + * @param cb.err - Error information when the method fails. + * @param cb.formAnswers - FormAnswers array object or null if form not exists. + */ + public readAll(formId: number, cb: (err: Error, formAnswers?: FormAnswer[]) => void) { + waterfall([ + (callback: (err: Error, result?: QueryResult) => void) => { + this.begin((error: Error) => { + callback(error); + }); + }, + (callback: (err: Error, result?: number[]) => void) => { + this.getFormAnswerIDs(formId, (err: Error, result: number[]) => { + callback(err, result); + }); + }, + (formAnswerIDs: number[], callback: (err: Error, result?: FormAnswerOptions[]) => void) => { + const formAnswers: FormAnswerOptions[] = []; + eachSeries(formAnswerIDs, (formAnswerId, innerCallback) => { + this.readController(formAnswerId, (error: Error, result?: FormAnswerOptions) => { + if (result) { + formAnswers.push(result); + } + + innerCallback(error); + }); + }, (error) => { + callback(error, formAnswers); + }); + }, + (formAnswer: FormAnswerOptions[], callback: (err: Error, result?: FormAnswerOptions[]) => void) => { + this.commit((error: Error) => { + callback(error, formAnswer); + }); + }, + (formAnswersOpt: FormAnswerOptions[], callback: (err: Error, result?: FormAnswer[]) => void) => { + + const formAnswers: FormAnswer[] = []; + + eachSeries(formAnswersOpt, (formAnswer, innerCallback) => { + this.formQueryBuilder.read(formAnswer.form.id, (error: Error, resultForm?: Form) => { + + const formAnswerObj: FormAnswerOptions = { + id: formAnswer.id + , form: resultForm + , timestamp: formAnswer.timestamp + , inputsAnswerOptions: formAnswer.inputsAnswerOptions + }; + + formAnswers.push(new FormAnswer(formAnswerObj)); + innerCallback(error); + }); + }, (error) => { + callback(error, formAnswers); + }); + } + ], (err, formAnswers?: FormAnswer[]) => { + if (err) { + this.rollback(() => { + cb(err); + }); + return; + } + cb(null, formAnswers); + }); + } + /** * Asynchronously read a FormAnswer from database without transaction. * @param formAnswerId - FormAnswer identifier to be founded. * @param cb - Callback function which contains the data read. * @param cb.err - Error information when the method fails. - * @param cb.result - FormAnswer object or null if FormAnswer not exists. + * @param cb.resultAnswer - FormAnswer object or null if FormAnswer not exists. */ - private readController(formAnswerId: number, cb: (err: Error, result?: FormAnswer) => void) { + private readController(formAnswerId: number, cb: (err: Error, resultAnswer?: FormAnswerOptions) => void) { waterfall([ - (callback: (err: Error, result?: InputAnswerOptionsDict) => void) => { + (callback: (err: Error, result?: InputAnswerOptions[]) => void) => { this.executeReadInput(formAnswerId, (err: Error, result?: QueryResult) => { if (err) { cb(err); return; } + + if (result.rowCount === 0) { + cb(ErrorHandler.notFound("Form Answer")); + return; + } + const inputAnswersOpts: InputAnswerOptions[] = result.rows.map((inputsAnswerResult) => { + let subForm: FormAnswerOptions = null; + if (inputsAnswerResult["id_sub_form"]) { + subForm = { + id: parseInt(inputsAnswerResult["id_sub_form"], 10) + , form: new Form({ + id: 0 + , title: "" + , description: "" + , inputs: [] + }) + , timestamp: new Date() + , inputsAnswerOptions: [] + }; + } const inputAnswersOpt: InputAnswerOptions = { id: inputsAnswerResult["id"] , idInput: inputsAnswerResult["id_input"] , value: inputsAnswerResult["value"] , placement: inputsAnswerResult["placement"] + , subForm }; return OptHandler.inputAnswer(inputAnswersOpt); }); - + callback(null, inputAnswersOpts); + }); + }, + (inputAnswersTmp: InputAnswerOptions[], callback: (err: Error, result?: InputAnswerOptionsDict) => void) => { + const inputAnswersOpts: InputAnswerOptions[] = []; + eachSeries(inputAnswersTmp, (inputAnswer, innerCallback) => { + this.readSubFormController(inputAnswer, inputAnswersOpts, innerCallback); + }, () => { const inputsAnswerResults: InputAnswerOptionsDict = {}; - for (const i of inputAnswersOpts){ + for (const i of inputAnswersOpts) { if (inputsAnswerResults[i["idInput"]]) { inputsAnswerResults[i["idInput"]].push(i); inputsAnswerResults[i["idInput"]] = Sorter.sortByPlacement(inputsAnswerResults[i["idInput"]]); @@ -269,33 +456,48 @@ export class AnswerQueryBuilder extends QueryBuilder { inputsAnswerResults[i["idInput"]] = [i]; } } - - callback(err, inputsAnswerResults); + callback(null, inputsAnswerResults); }); }, - (inputsAnswerResults: InputAnswerOptionsDict, callback: (err: Error, result?: FormAnswer) => void) => { - this.executeReadForm(formAnswerId, (error: Error, answerResult?: any) => { - if (error) { - callback(error); - return; + (inputsAnswerResults: InputAnswerOptionsDict, callback: (err: Error, result?: FormAnswerOptions) => void) => { + waterfall([ + (innerCallback: (err: Error, formAnswer?: FormAnswerOptions) => void) => { + this.executeReadForm(formAnswerId, (error: Error, answerResult?: any) => { + if (error) { + innerCallback(error); + return; + } + const formTmp: Form = { + id: answerResult.id_form + , title: undefined + , description: undefined + , inputs: [] + }; + const formAnswerTmp: FormAnswerOptions = { + id: answerResult.id + , form: formTmp + , timestamp: answerResult.answered_at + , inputsAnswerOptions: inputsAnswerResults + }; + innerCallback(null, formAnswerTmp); + }); + }, + (formAnswer: FormAnswerOptions, innerCallback: (err: Error, formAnswer?: FormAnswerOptions) => void) => { + this.formQueryBuilder.readController(formAnswer.form.id, (err: Error, form?: Form) => { + const formAnswerTmp: FormAnswerOptions = { + id: formAnswer.id + , form + , timestamp: formAnswer.timestamp + , inputsAnswerOptions: formAnswer.inputsAnswerOptions + }; + innerCallback(null, formAnswerTmp); + }); } - const formTmp: Form = { - id: answerResult.id_form - , title: undefined - , description: undefined - , inputs: [] - }; - const formAnswerTmp: FormAnswerOptions = { - id: answerResult.id - , form: formTmp - , timestamp: answerResult.answered_at - , inputsAnswerOptions: inputsAnswerResults - }; - - callback(null, new FormAnswer(formAnswerTmp)); + ], (err, formAnswer: FormAnswerOptions) => { + callback(err, formAnswer); }); } - ], (err, formAnswer: FormAnswer) => { + ], (err, formAnswer: FormAnswerOptions) => { if (err) { cb(err); return; @@ -304,9 +506,60 @@ export class AnswerQueryBuilder extends QueryBuilder { }); } + private getFormAnswerIDs(formId: number, cb: (err: Error, formAnswerIds: number[]) => void) { + const queryString: string = "SELECT form_answer.id FROM form \ + INNER JOIN form_answer \ + ON form_answer.id_form=form.id \ + WHERE form.id=$1;"; + const query: QueryOptions = { + query: queryString + , parameters: [formId] + }; + + this.executeQuery(query, (error: Error, result?: QueryResult) => { + const ids: number[] = []; + for (const i of result.rows) { + ids.push(i.id); + } + cb(error, ids); + }); + } + /** - * Asynchronously read a formAnswer from database. - * @param formUpdate - FormAnswer identifier to be founded. + * Asynchronously read a SubFormAnswer (FormAnswer object) on database without transactions. + * @param inputAnswer - InputAnswerOptions object that contains the subForm (if not null) that should be readed. + * @param inputAnswerArray - InputAnswerOptions array that contains the inputs already validated. + * @param cb - Callback function which contains the data read. + * @param cb.err - Error information when the method fails. + */ + private readSubFormController(inputAnswer: InputAnswerOptions, inputAnswerArray: InputAnswerOptions[], cb: (err: Error) => void) { + if (inputAnswer.subForm === null ) { + inputAnswerArray.push(inputAnswer); + cb(null); + return; + } + + this.readController(inputAnswer.subForm.id, (err: Error, formAnswer?: FormAnswerOptions) => { + if (err) { + cb(err); + return; + } + const inputAnswerTmp: InputAnswerOptions = { + id: inputAnswer.id + , idInput: inputAnswer.idInput + , placement: inputAnswer.placement + , value: inputAnswer.value + , subForm: formAnswer + }; + inputAnswerArray.push(inputAnswerTmp); + cb(null); + return; + }); + } + + /** + * Asynchronously read a FormAnswer from database. + * @param formAnswerId - FormAnswer identifier to be founded. * @param cb - Callback function. * @param cb.err - Error information when the method fails. * @param cb.result - The read FormAnswer result query row. @@ -331,14 +584,14 @@ export class AnswerQueryBuilder extends QueryBuilder { } /** - * Asynchronously read a InputAnswer from database. + * Asynchronously read a InputAnswer from database without transactions. * @param formAnswerId - Identifier to read InputAnswers. * @param cb - Callback function. * @param cb.err - Error information when the method fails. * @param cb.result - The read InputAnswer result query. */ private executeReadInput(formAnswerId: number, cb: (err: Error, result?: QueryResult) => void) { - const queryString: string = "SELECT id, id_form_answer, id_input, value, placement \ + const queryString: string = "SELECT id, id_form_answer, id_input, id_sub_form, value, placement \ FROM input_answer \ WHERE id_form_answer=$1;"; const query: QueryOptions = { diff --git a/src/utils/config.spec.ts b/src/utils/config.spec.ts index 3daebb1368f5adf30c78a566d116ddf957a60ad3..3a5580445727252712cad91ed48bb6f4cb0fe672 100644 --- a/src/utils/config.spec.ts +++ b/src/utils/config.spec.ts @@ -24,10 +24,10 @@ describe("Config Handler", () => { it("should test configs", () => { - expect(configs.poolconfig["user"]).to.be.equal(process.env["POSTGRES_USER"]); - expect(configs.poolconfig["host"]).to.be.equal(process.env["POSTGRES_HOST"]); - expect(configs.poolconfig["database"]).to.be.equal(process.env["POSTGRES_DB"]); - expect(configs.poolconfig["password"]).to.be.equal(process.env["POSTGRES_PASSWORD"]); - expect(configs.poolconfig["port"]).to.be.equal(parseInt(process.env["POSTGRES_PORT"], 10)); + expect(configs.poolconfig["user"]).to.be.equal(process.env["DB_USER"]); + expect(configs.poolconfig["host"]).to.be.equal(process.env["DB_HOST"]); + expect(configs.poolconfig["database"]).to.be.equal(process.env["DB_NAME"]); + expect(configs.poolconfig["password"]).to.be.equal(process.env["DB_PASSWORD"]); + expect(configs.poolconfig["port"]).to.be.equal(parseInt(process.env["DB_PORT"], 10)); }); }); diff --git a/src/utils/config.ts b/src/utils/config.ts index ce2eef0aa7496ac0a65c3b959576cca8073ff59c..c7b46c1c15cbcf99c53f7a0cfb55b0d70ec5adef 100644 --- a/src/utils/config.ts +++ b/src/utils/config.ts @@ -31,11 +31,11 @@ class Config { public readonly poolconfig: PoolConfig; private constructor(){ - this.user = process.env["POSTGRES_USER"]; - this.host = process.env["POSTGRES_HOST"]; - this.database = process.env["POSTGRES_DB"]; - this.password = process.env["POSTGRES_PASSWORD"]; - this.port = parseInt(process.env["POSTGRES_PORT"], 10); + this.user = process.env["DB_USER"]; + this.host = process.env["DB_HOST"]; + this.database = process.env["DB_NAME"]; + this.password = process.env["DB_PASSWORD"]; + this.port = parseInt(process.env["DB_PORT"], 10); this.poolconfig = { user: this.user, database: this.database, @@ -44,8 +44,7 @@ class Config { port: this.port, max: 10, idleTimeoutMillis: 3000 - }; - + }; } public static get Configurations(){ diff --git a/src/utils/dbHandler.spec.ts b/src/utils/dbHandler.spec.ts index d9301a62bde28b94dd7fe579d1bd3e272f3d9d52..35f07ca94463d16f3cdd643c3f973cccada7ba22 100644 --- a/src/utils/dbHandler.spec.ts +++ b/src/utils/dbHandler.spec.ts @@ -18,30 +18,30 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ -import { series } from "async"; +import { series, waterfall } from "async"; import { expect } from "chai"; import { QueryResult } from "pg"; +import { dbHandlerScenario } from "../../test/scenario"; import { Form, FormOptions } from "../core/form"; import { FormAnswer, FormAnswerOptions } from "../core/formAnswer"; import { FormUpdate, FormUpdateOptions } from "../core/formUpdate"; import { Input, InputOptions, Validation } from "../core/input"; import { InputAnswer, InputAnswerDict, InputAnswerOptions, InputAnswerOptionsDict } from "../core/inputAnswer"; import { InputUpdate, InputUpdateOptions } from "../core/inputUpdate"; +import { SubForm, SubFormOptions } from "../core/subForm"; +import { User, UserOptions } from "../core/user"; import { configs } from "./config"; import { DbHandler } from "./dbHandler"; import { InputType, UpdateType, ValidationType } from "./enumHandler"; +import { ErrorHandler } from "./errorHandler"; import { OptHandler } from "./optHandler"; import { QueryBuilder, QueryOptions } from "./queryBuilder"; import { TestHandler } from "./testHandler"; describe("Database Handler", () => { const dbhandler = new DbHandler(configs.poolconfig); - - it("should insert a form", (done) => { - const queryString: string = "INSERT INTO form(id, title, description)\ - VALUES (5, 'Form Title 5', 'Form Description 5');"; - const query: QueryOptions = {query: queryString, parameters: []}; - dbhandler.form.executeQuery(query, (err: Error, result?: QueryResult) => { + it("should insert a form, id = 5", (done) => { + dbhandler.form.executeQuery(dbHandlerScenario.insertForm5, (err: Error, result?: QueryResult) => { expect(err).to.be.a("null"); expect(result.command).to.be.equal("INSERT"); expect(result.rowCount).to.be.equal(1); @@ -50,23 +50,18 @@ describe("Database Handler", () => { }); }); - it("should insert a form and then rollback", (done) => { + it("should insert a form, id = 6, and then rollback", (done) => { series([ (cb: (err: Error, result?: QueryResult) => void) => { dbhandler.form.begin(cb); }, (callback: (err: Error, result?: QueryResult) => void) => { - const queryString: string = "INSERT INTO form(id, title, description)\ - VALUES (6, 'Form Title 6', 'Form Description 6');"; - const query: QueryOptions = {query: queryString, parameters: []}; - - dbhandler.form.executeQuery(query, callback); + dbhandler.form.executeQuery(dbHandlerScenario.insertForm6, callback); }, (cb: (err: Error, result?: QueryResult) => void) => { dbhandler.form.rollback(cb); } ], (err, results) => { - expect(err).to.be.a("null"); expect(results[0].command).to.be.equal("BEGIN"); expect(results[1].command).to.be.equal("INSERT"); @@ -78,9 +73,7 @@ describe("Database Handler", () => { }); it("should select all forms", (done) => { - const queryString: string = "SELECT * FROM form;"; - const query: QueryOptions = {query: queryString, parameters: []}; - dbhandler.form.executeQuery(query, (err: Error, result?: QueryResult) => { + dbhandler.form.executeQuery(dbHandlerScenario.selectAllForm, (err: Error, result?: QueryResult) => { expect(err).to.be.a("null"); expect(result.command).to.be.equal("SELECT"); expect(result.rowCount).to.be.equal(5); @@ -89,12 +82,8 @@ describe("Database Handler", () => { }); }); - it("should remove non existent form", (done) => { - const queryString: string = "DELETE FROM form WHERE id=6;"; - - const query: QueryOptions = {query: queryString, parameters: []}; - - dbhandler.form.executeQuery(query, (err: Error, result?: QueryResult) => { + it("should remove non existent form with id = 6", (done) => { + dbhandler.form.executeQuery(dbHandlerScenario.deleteForm6, (err: Error, result?: QueryResult) => { expect(err).to.be.a("null"); expect(result.command).to.be.equal("DELETE"); expect(result.rowCount).to.be.equal(0); @@ -103,10 +92,8 @@ describe("Database Handler", () => { }); }); - it("should remove existent form", (done) => { - const queryString: string = "DELETE FROM form WHERE id=5;"; - const query: QueryOptions = {query: queryString, parameters: []}; - dbhandler.form.executeQuery(query, (err: Error, result?: QueryResult) => { + it("should remove existent form with id = 5", (done) => { + dbhandler.form.executeQuery(dbHandlerScenario.deleteForm5, (err: Error, result?: QueryResult) => { expect(err).to.be.a("null"); expect(result.command).to.be.equal("DELETE"); expect(result.rowCount).to.be.equal(1); @@ -116,12 +103,7 @@ describe("Database Handler", () => { }); it("should insert a input", (done) => { - const queryString: string = "INSERT INTO input(id_form, placement, input_type, enabled, question, description)\ - VALUES\ - (2, 3,'TEXT', TRUE, 'Question 3 Form 2', 'Description Question 3 Form 2'),\ - (2, 4,'TEXT', TRUE, 'Question 4 Form 2', 'Description Question 4 Form 2');"; - const query: QueryOptions = {query: queryString, parameters: []}; - dbhandler.form.executeQuery(query, (err: Error, result?: QueryResult) => { + dbhandler.form.executeQuery(dbHandlerScenario.insertInputs, (err: Error, result?: QueryResult) => { expect(err).to.be.a("null"); expect(result.command).to.be.equal("INSERT"); expect(result.rowCount).to.be.equal(2); @@ -130,10 +112,7 @@ describe("Database Handler", () => { }); it("should select all inputs", (done) => { - const queryString: string = "SELECT * FROM input;"; - const query: QueryOptions = {query: queryString, parameters: []}; - - dbhandler.form.executeQuery(query, (err: Error, result?: QueryResult) => { + dbhandler.form.executeQuery(dbHandlerScenario.selectAllInputs, (err: Error, result?: QueryResult) => { expect(err).to.be.a("null"); expect(result.command).to.be.equal("SELECT"); expect(result.rowCount).to.be.equal(15); @@ -143,10 +122,7 @@ describe("Database Handler", () => { }); it("should remove non existent input", (done) => { - const queryString: string = "DELETE FROM input WHERE id=20;"; - const query: QueryOptions = {query: queryString, parameters: []}; - - dbhandler.form.executeQuery(query, (err: Error, result?: QueryResult) => { + dbhandler.form.executeQuery(dbHandlerScenario.deleteNonExistentInput, (err: Error, result?: QueryResult) => { expect(err).to.be.a("null"); expect(result.command).to.be.equal("DELETE"); expect(result.rowCount).to.be.equal(0); @@ -155,10 +131,7 @@ describe("Database Handler", () => { }); it("should remove existent input", (done) => { - const queryString: string = "DELETE FROM input WHERE id=9 OR id=14 OR id=15;"; - const query: QueryOptions = {query: queryString, parameters: []}; - - dbhandler.form.executeQuery(query, (err: Error, result?: QueryResult) => { + dbhandler.form.executeQuery(dbHandlerScenario.deleteBothInputs, (err: Error, result?: QueryResult) => { expect(err).to.be.a("null"); expect(result.command).to.be.equal("DELETE"); expect(result.rowCount).to.be.equal(3); @@ -168,13 +141,7 @@ describe("Database Handler", () => { }); it("should insert a input validations", (done) => { - const queryString: string = "INSERT INTO input_validation(id_input, validation_type)\ - VALUES\ - (2, 'MAXCHAR'),\ - (5, 'MANDATORY');"; - const query: QueryOptions = {query: queryString, parameters: []}; - - dbhandler.form.executeQuery(query, (err: Error, result?: QueryResult) => { + dbhandler.form.executeQuery(dbHandlerScenario.insertInputValidations, (err: Error, result?: QueryResult) => { expect(err).to.be.a("null"); expect(result.command).to.be.equal("INSERT"); expect(result.rowCount).to.be.equal(2); @@ -183,22 +150,16 @@ describe("Database Handler", () => { }); it("should select all input validations", (done) => { - const queryString: string = "SELECT * FROM input_validation;"; - const query: QueryOptions = {query: queryString, parameters: []}; - - dbhandler.form.executeQuery(query, (err: Error, result?: QueryResult) => { + dbhandler.form.executeQuery(dbHandlerScenario.selectInputValidations, (err: Error, result?: QueryResult) => { expect(err).to.be.a("null"); expect(result.command).to.be.equal("SELECT"); - expect(result.rowCount).to.be.equal(14); + expect(result.rowCount).to.be.equal(18); done(); }); }); it("should remove non existent input validations", (done) => { - const queryString: string = "DELETE FROM input_validation WHERE id=21;"; - const query: QueryOptions = {query: queryString, parameters: []}; - - dbhandler.form.executeQuery(query, (err: Error, result?: QueryResult) => { + dbhandler.form.executeQuery(dbHandlerScenario.deleteNonExistetnValidations, (err: Error, result?: QueryResult) => { expect(err).to.be.a("null"); expect(result.command).to.be.equal("DELETE"); expect(result.rowCount).to.be.equal(0); @@ -207,25 +168,16 @@ describe("Database Handler", () => { }); it("should remove existent input validations", (done) => { - const queryString: string = "DELETE FROM input_validation WHERE id=9 OR id=10 OR id=13 OR id=14;"; - const query: QueryOptions = {query: queryString, parameters: []}; - - dbhandler.form.executeQuery(query, (err: Error, result?: QueryResult) => { + dbhandler.form.executeQuery(dbHandlerScenario.deleteInputValidations, (err: Error, result?: QueryResult) => { expect(err).to.be.a("null"); expect(result.command).to.be.equal("DELETE"); - expect(result.rowCount).to.be.equal(4); + expect(result.rowCount).to.be.equal(5); done(); }); }); it("should insert a input validations arguments", (done) => { - const queryString: string = "INSERT INTO input_validation_argument(id_input_validation, placement, argument)\ - VALUES\ - (1, 2, '10'),\ - (2, 2, '2');"; - const query: QueryOptions = {query: queryString, parameters: []}; - - dbhandler.form.executeQuery(query, (err: Error, result?: QueryResult) => { + dbhandler.form.executeQuery(dbHandlerScenario.insertInputValArguments, (err: Error, result?: QueryResult) => { expect(err).to.be.a("null"); expect(result.command).to.be.equal("INSERT"); expect(result.rowCount).to.be.equal(2); @@ -235,22 +187,16 @@ describe("Database Handler", () => { }); it("should select all input validations arguments", (done) => { - const queryString: string = "SELECT * FROM input_validation_argument;"; - const query: QueryOptions = {query: queryString, parameters: []}; - - dbhandler.form.executeQuery(query, (err: Error, result?: QueryResult) => { + dbhandler.form.executeQuery(dbHandlerScenario.selectInputValidationArgumetns, (err: Error, result?: QueryResult) => { expect(err).to.be.a("null"); expect(result.command).to.be.equal("SELECT"); - expect(result.rowCount).to.be.equal(9); + expect(result.rowCount).to.be.equal(13); done(); }); }); it("should remove non existent input validations arguments", (done) => { - const queryString: string = "DELETE FROM input_validation_argument WHERE id=15;"; - const query: QueryOptions = {query: queryString, parameters: []}; - - dbhandler.form.executeQuery(query, (err: Error, result?: QueryResult) => { + dbhandler.form.executeQuery(dbHandlerScenario.deleteNEInputValArgs, (err: Error, result?: QueryResult) => { expect(err).to.be.a("null"); expect(result.command).to.be.equal("DELETE"); expect(result.rowCount).to.be.equal(0); @@ -259,10 +205,7 @@ describe("Database Handler", () => { }); it("should remove existent input validations arguments", (done) => { - const queryString: string = "DELETE FROM input_validation_argument WHERE id=6;"; - const query: QueryOptions = {query: queryString, parameters: []}; - - dbhandler.form.executeQuery(query, (err: Error, result?: QueryResult) => { + dbhandler.form.executeQuery(dbHandlerScenario.deleteInputValArgs, (err: Error, result?: QueryResult) => { expect(err).to.be.a("null"); expect(result.command).to.be.equal("DELETE"); expect(result.rowCount).to.be.equal(1); @@ -271,13 +214,7 @@ describe("Database Handler", () => { }); it("should insert a form answers", (done) => { - const queryString: string = "INSERT INTO form_answer(id ,id_form, answered_at)\ - VALUES\ - (8, 2, '2018-07-02 10:10:25-03'),\ - (9, 3, '2018-06-03 10:11:25-03');"; - const query: QueryOptions = {query: queryString, parameters: []}; - - dbhandler.answer.executeQuery(query, (err: Error, result?: QueryResult) => { + dbhandler.answer.executeQuery(dbHandlerScenario.insertFormAnswers, (err: Error, result?: QueryResult) => { expect(err).to.be.a("null"); expect(result.command).to.be.equal("INSERT"); expect(result.rowCount).to.be.equal(2); @@ -286,10 +223,7 @@ describe("Database Handler", () => { }); it("should select all form answers", (done) => { - const queryString: string = "SELECT * FROM form_answer;"; - const query: QueryOptions = {query: queryString, parameters: []}; - - dbhandler.answer.executeQuery(query, (err: Error, result?: QueryResult) => { + dbhandler.answer.executeQuery(dbHandlerScenario.selectFormAnswers, (err: Error, result?: QueryResult) => { expect(err).to.be.a("null"); expect(result.command).to.be.equal("SELECT"); expect(result.rowCount).to.be.equal(9); @@ -298,10 +232,7 @@ describe("Database Handler", () => { }); it("should remove non existent form answer", (done) => { - const queryString: string = "DELETE FROM form_answer WHERE id=11;"; - const query: QueryOptions = {query: queryString, parameters: []}; - - dbhandler.answer.executeQuery(query, (err: Error, result?: QueryResult) => { + dbhandler.answer.executeQuery(dbHandlerScenario.deleteNEFormAnswers, (err: Error, result?: QueryResult) => { expect(err).to.be.a("null"); expect(result.command).to.be.equal("DELETE"); expect(result.rowCount).to.be.equal(0); @@ -310,10 +241,7 @@ describe("Database Handler", () => { }); it("should remove existent form answers", (done) => { - const queryString: string = "DELETE FROM form_answer WHERE id=8 OR id=9;"; - const query: QueryOptions = {query: queryString, parameters: []}; - - dbhandler.answer.executeQuery(query, (err: Error, result?: QueryResult) => { + dbhandler.answer.executeQuery(dbHandlerScenario.deleteFormAnswers, (err: Error, result?: QueryResult) => { expect(err).to.be.a("null"); expect(result.command).to.be.equal("DELETE"); expect(result.rowCount).to.be.equal(2); @@ -323,13 +251,7 @@ describe("Database Handler", () => { }); it("should insert a input answers", (done) => { - const queryString: string = "INSERT INTO input_answer(id, id_form_answer, id_input, value, placement)\ - VALUES\ - (18,1, 6,'Answer to Question 1 Form 3',1),\ - (19,1, 7,'Answer to Question 2 Form 3',2);"; - const query: QueryOptions = {query: queryString, parameters: []}; - - dbhandler.answer.executeQuery(query, (err: Error, result?: QueryResult) => { + dbhandler.answer.executeQuery(dbHandlerScenario.insertInputAnswers, (err: Error, result?: QueryResult) => { expect(err).to.be.a("null"); expect(result.command).to.be.equal("INSERT"); expect(result.rowCount).to.be.equal(2); @@ -338,10 +260,7 @@ describe("Database Handler", () => { }); it("should select all form answers", (done) => { - const queryString: string = "SELECT * FROM input_answer;"; - const query: QueryOptions = {query: queryString, parameters: []}; - - dbhandler.answer.executeQuery(query, (err: Error, result?: QueryResult) => { + dbhandler.answer.executeQuery(dbHandlerScenario.selectInputAnswers, (err: Error, result?: QueryResult) => { expect(err).to.be.a("null"); expect(result.command).to.be.equal("SELECT"); expect(result.rowCount).to.be.equal(19); @@ -350,10 +269,7 @@ describe("Database Handler", () => { }); it("should remove non existent input answer", (done) => { - const queryString: string = "DELETE FROM input_answer WHERE id=25;"; - const query: QueryOptions = {query: queryString, parameters: []}; - - dbhandler.answer.executeQuery(query, (err: Error, result?: QueryResult) => { + dbhandler.answer.executeQuery(dbHandlerScenario.removeNEInputAnswers, (err: Error, result?: QueryResult) => { expect(err).to.be.a("null"); expect(result.command).to.be.equal("DELETE"); expect(result.rowCount).to.be.equal(0); @@ -362,10 +278,7 @@ describe("Database Handler", () => { }); it("should remove existent input answers", (done) => { - const queryString: string = "DELETE FROM input_answer WHERE id=18 OR id=19;"; - const query: QueryOptions = {query: queryString, parameters: []}; - - dbhandler.answer.executeQuery(query, (err: Error, result?: QueryResult) => { + dbhandler.answer.executeQuery(dbHandlerScenario.removeInputAnswers, (err: Error, result?: QueryResult) => { expect(err).to.be.a("null"); expect(result.command).to.be.equal("DELETE"); expect(result.rowCount).to.be.equal(2); @@ -377,77 +290,17 @@ describe("Database Handler", () => { describe("Read and Write on Database", () => { const dbhandler = new DbHandler(configs.poolconfig); - it("should list all forms", (done) => { - dbhandler.form.list((err: Error, forms?: Form[]) => { - expect(err).to.be.a("null"); - expect(forms.length).to.be.equal(4); - for (let i = 0; i < forms.length; i++){ - expect(forms[i].id).to.be.equal(i + 1); - expect(forms[i].title).to.be.equal("Form Title " + (i + 1)); - expect(forms[i].description).to.be.equal("Form Description " + (i + 1)); - } - done(); - }); - }); it("should read an existent form", (done) => { dbhandler.form.read(1, (err: Error, form: Form) => { - expect(err).to.be.a("null"); - - const inputObj1: InputOptions = { - placement: 0 - , description: "Description Question 1 Form 1" - , question: "Question 1 Form 1" - , type: InputType.TEXT - , validation: [] - , sugestions: [] - , id: 1 - }; - - const inputObj2: InputOptions = { - placement: 1 - , description: "Description Question 3 Form 1" - , question: "Question 3 Form 1" - , type: InputType.TEXT - , validation: [ - { type: ValidationType.REGEX, arguments: ["\\d{5}-\\d{3}"] } - , { type: ValidationType.MANDATORY, arguments: [] } - ] - , sugestions: [] - , id: 2 - }; - - const inputObj3: InputOptions = { - placement: 2 - , description: "Description Question 2 Form 1" - , question: "Question 2 Form 1" - , type: InputType.TEXT - , validation: [ - { type: ValidationType.MAXCHAR, arguments: ["10"] } - , { type: ValidationType.MINCHAR, arguments: ["2"] } - ] - , sugestions: [] - , id: 3 - }; - - const formObj: FormOptions = { - id: 1 - , title: "Form Title 1" - , description: "Form Description 1" - , inputs: [ - OptHandler.input(inputObj1) - , OptHandler.input(inputObj2) - , OptHandler.input(inputObj3) - ] - }; - TestHandler.testForm(form, new Form(formObj)); + TestHandler.testForm(form, new Form(dbHandlerScenario.formToRead)); done(); }); }); it("should read a non existent form", (done) => { - dbhandler.form.read(10, (err: Error, form?: Form) => { + dbhandler.form.read(20, (err: Error, form?: Form) => { expect(err).to.be.not.equal(null); expect(form).to.be.undefined; done(); @@ -455,39 +308,7 @@ describe("Read and Write on Database", () => { }); it("should write form", (done) => { - - const inputObj1: InputOptions = { - placement: 0 - , description: "Description Question 1 Form 1" - , question: "Question 1 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: ValidationType.MANDATORY, arguments: [] } - ] - }; - - const inputObj2 = { - placement: 1 - , description: "Description Question 2 Form 2" - , question: "Question 2 Form 2" - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: ValidationType.MINCHAR, arguments: ["5"] } - ] - }; - - const formObj: FormOptions = { - title: "Form Title 1" - , description: "Form Description 1" - , inputs: [ - OptHandler.input(inputObj1) - , OptHandler.input(inputObj2) - ] - }; - - const form = new Form(OptHandler.form(formObj)); + const form = new Form(OptHandler.form(dbHandlerScenario.formToWrite)); dbhandler.form.write(form, (err: Error, formResult: Form) => { expect(err).to.be.a("null"); expect(formResult.id).to.be.equal(5); @@ -501,39 +322,12 @@ describe("Read and Write on Database", () => { }); it("should read an existent form Answer", (done) => { - - const inputAnswersOpt1: InputAnswerOptions = { - id: 5 - , idInput: 1 - , placement: 0 - , value: "Answer to Question 1 Form 1" - }; - const inputAnswersOpt2: InputAnswerOptions = { - id: 6 - , idInput: 2 - , placement: 0 - , value: "Answer to Question 2 Form 1" - }; - const inputAnswersOpt3: InputAnswerOptions = { - id: 7 - , idInput: 3 - , placement: 0 - , value: "Answer to Question 3 Form 1" - }; - - const inputAnswerOptionsDict: InputAnswerOptionsDict = { - 1: [inputAnswersOpt1] - , 2: [inputAnswersOpt2] - , 3: [inputAnswersOpt3] - }; - - const date: Date = new Date("2019-02-21 12:10:25"); dbhandler.form.read(1, (error: Error, form: Form) => { const formAnswerOptions: FormAnswerOptions = { id: 3 , form - , timestamp: date - , inputsAnswerOptions: inputAnswerOptionsDict + , timestamp: dbHandlerScenario.date + , inputsAnswerOptions: dbHandlerScenario.inputAnswerToRead }; dbhandler.answer.read(3, (err: Error, formAnswer: FormAnswer) => { TestHandler.testFormAnswer(formAnswer, new FormAnswer(formAnswerOptions)); @@ -553,39 +347,11 @@ describe("Read and Write on Database", () => { }); it("should write form Answer", (done) => { - - const inputAnswersOpt1: InputAnswerOptions = { - id: 5 - , idInput: 1 - , placement: 0 - , value: "Answer to Question 1 Form 1" - }; - - const inputAnswersOpt2: InputAnswerOptions = { - id: 6 - , idInput: 2 - , placement: 0 - , value: "Answer to Question 2 Form 1" - }; - - const inputAnswersOpt3: InputAnswerOptions = { - id: 7 - , idInput: 3 - , placement: 0 - , value: "Answer to Question 3 Form 1" - }; - - const inputAnswerOptionsDict: InputAnswerOptionsDict = { - 1: [inputAnswersOpt1] - , 2: [inputAnswersOpt2] - , 3: [inputAnswersOpt3] - }; - dbhandler.form.read(1, (error: Error, form: Form) => { const formAnswerOptions: FormAnswerOptions = { form , timestamp: new Date() - , inputsAnswerOptions: inputAnswerOptionsDict + , inputsAnswerOptions: dbHandlerScenario.inputAnswerToWrite }; const formAnswerObj = new FormAnswer(formAnswerOptions); @@ -605,555 +371,55 @@ describe("Read and Write on Database", () => { }); it("should update a form and insert FormUpdate", (done) => { - const inputObj1: InputOptions = { - placement: 0 - , description: "Description Question 1 Form 1" - , question: "Question 1 Form 1" - , type: InputType.TEXT - , validation: [] - , id: 1 - }; - const updateObj1: InputUpdateOptions = { - id: 1 - , input: inputObj1 - , inputOperation: UpdateType.REMOVE - , value: null - }; - - const inputObj2: InputOptions = { - placement: 1 - , description: "Description Question 2 Form 1" - , question: "Question 2 Form 1" - , type: InputType.TEXT - , validation: [ - { type: ValidationType.MAXCHAR, arguments: ["10"] } - , { type: ValidationType.MINCHAR, arguments: ["2"] } - ] - , id: 3 - }; - const updateObj2: InputUpdateOptions = { - id: 2 - , input: inputObj2 - , inputOperation: UpdateType.REMOVE - , value: null - }; - - const inputObj3: InputOptions = { - placement: 2 - , description: "Description Question 3 Form 1" - , question: "Question 3 Form 1" - , type: InputType.TEXT - , validation: [ - { type: ValidationType.REGEX, arguments: ["\\d{5}-\\d{3}"] } - , { type: ValidationType.MANDATORY, arguments: [] } - ] - , id: 2 - }; - const updateObj3: InputUpdateOptions = { - id: 3 - , input: inputObj3 - , inputOperation: UpdateType.REMOVE - , value: null - }; - - const formObj: FormOptions = { - id: 1 - , title: "Form Title 1" - , description: "Form Description 1" - , inputs: [ - inputObj1 - , inputObj2 - , inputObj3 - ] - }; - const formUpdateObj: FormUpdateOptions = { - id: 1 - , form: formObj - , updateDate: new Date() - , changed: true - , inputUpdates: [ - updateObj1 - , updateObj2 - , updateObj3 - ] - }; - - dbhandler.form.update(new FormUpdate (formUpdateObj), (err: Error) => { + dbhandler.form.update(new FormUpdate (dbHandlerScenario.updateForm), (err: Error) => { expect(err).to.be.a("null"); done(); }); }); it("should update inputs and insert FormUpdate", (done) => { - const inputObj1: InputOptions = { - placement: 0 - , description: "Description Question 1 Form 1" - , question: "Question 1 Form 1" - , type: InputType.TEXT - , validation: [] - , id: 1 - }; - const updateObj1: InputUpdateOptions = { - id: 1 - , input: inputObj1 - , inputOperation: UpdateType.REMOVE - , value: null - }; - - const inputObj2: InputOptions = { - placement: 1 - , description: "Description Question 2 Form 1" - , question: "Question 2 Form 1" - , type: InputType.TEXT - , validation: [ - { type: ValidationType.MAXCHAR, arguments: ["10"] } - , { type: ValidationType.MINCHAR, arguments: ["2"] } - ] - , id: 3 - }; - const updateObj2: InputUpdateOptions = { - id: 2 - , input: inputObj2 - , inputOperation: UpdateType.REMOVE - , value: null - }; - - const inputObj3: InputOptions = { - placement: 2 - , description: "Description Question 3 Form 1" - , question: "Question 3 Form 1" - , type: InputType.TEXT - , validation: [ - { type: ValidationType.REGEX, arguments: ["\\d{5}-\\d{3}"] } - , { type: ValidationType.MANDATORY, arguments: [] } - ] - , id: 2 - }; - const updateObj3: InputUpdateOptions = { - id: 3 - , input: inputObj3 - , inputOperation: UpdateType.REMOVE - , value: null - }; - - const formObj: FormOptions = { - id: 1 - , title: "Form Title 1" - , description: "Form Description 1" - , inputs: [ - inputObj1 - , inputObj2 - , inputObj3 - ] - }; - const formUpdateObj: FormUpdateOptions = { - id: 1 - , form: formObj - , updateDate: new Date() - , changed: false - , inputUpdates: [ - updateObj1 - , updateObj2 - , updateObj3 - ] - }; - - dbhandler.form.update(new FormUpdate (formUpdateObj), (err: Error) => { + dbhandler.form.update(new FormUpdate (dbHandlerScenario.updateInput), (err: Error) => { expect(err).to.be.a("null"); done(); }); }); it("should reenabled inputs and insert FormUpdate", (done) => { - const inputObj1: InputOptions = { - placement: 0 - , description: "Description Question 1 Form 1" - , question: "Question 1 Form 1" - , type: InputType.TEXT - , validation: [] - , id: 1 - }; - const updateObj1: InputUpdateOptions = { - id: 1 - , input: inputObj1 - , inputOperation: UpdateType.REENABLED - , value: null - }; - - const inputObj2: InputOptions = { - placement: 1 - - , description: "Description Question 2 Form 1" - , question: "Question 2 Form 1" - , type: InputType.TEXT - , validation: [ - { type: ValidationType.MAXCHAR, arguments: ["10"] } - , { type: ValidationType.MINCHAR, arguments: ["2"] } - ] - , id: 3 - }; - const updateObj2: InputUpdateOptions = { - id: 2 - , input: inputObj2 - , inputOperation: UpdateType.REENABLED - , value: null - }; - - const inputObj3: InputOptions = { - placement: 2 - , description: "Description Question 3 Form 1" - , question: "Question 3 Form 1" - , type: InputType.TEXT - , validation: [ - { type: ValidationType.REGEX, arguments: ["\\d{5}-\\d{3}"] } - , { type: ValidationType.MANDATORY, arguments: [] } - ] - , id: 2 - }; - const updateObj3: InputUpdateOptions = { - id: 3 - , input: inputObj3 - , inputOperation: UpdateType.REENABLED - , value: null - }; - - const formObj: FormOptions = { - id: 1 - , title: "Form Title 1" - , description: "Form Description 1" - , inputs: [ - inputObj1 - , inputObj2 - , inputObj3 - ] - }; - const formUpdateObj: FormUpdateOptions = { - id: 1 - , form: formObj - , updateDate: new Date() - , changed: false - , inputUpdates: [ - updateObj1 - , updateObj2 - , updateObj3 - ] - }; - - dbhandler.form.update(new FormUpdate (formUpdateObj), (err: Error) => { + dbhandler.form.update(new FormUpdate (dbHandlerScenario.reenabledInputs), (err: Error) => { expect(err).to.be.a("null"); done(); }); }); it("should not update database operation not recognized", (done) => { - const inputObj: Input = { - placement: 0 - , description: "Description Question 1 Form 1" - , question: "Question 1 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [] - , id: 1 - }; - const formObj: Form = { - id: 1 - , title: "Form Title 1" - , description: "Form Description 1" - , inputs: [ inputObj ] - }; - - const inputUpdateObj: InputUpdate = { - input: inputObj - , inputOperation: 10 - , value: "Invalid Operation" - }; - const formUpdateObj: FormUpdate = { - form: formObj - , updateDate: new Date() - , inputUpdates: [ inputUpdateObj ] - }; - - dbhandler.form.update(formUpdateObj, (err: Error) => { + dbhandler.form.update(dbHandlerScenario.failedUpdate, (err: Error) => { expect(err).to.be.not.a("null"); done(); }); }); it("should insert a input with answer sugestions", (done) => { - - const inputObj1: Input = { - placement: 0 - , description: "Description Question 1 Form 6" - , question: "Question 1 Form 6" - , enabled: true - , type: InputType.CHECKBOX - , sugestions: [ - { value: "Sugestion 1", placement: 0 } - , { value: "Sugestion 2", placement: 1 } - , { value: "Sugestion 3", placement: 2 } - ] - , validation: [ - { type: ValidationType.SOMECHECKBOX, arguments: [] } - ] - , id: 18 - }; - - const inputObj2: Input = { - placement: 1 - , description: "Description Question 2 Form 6" - , question: "Question 2 Form 6" - , enabled: true - , type: InputType.RADIO - , sugestions: [ - { value: "Sugestion 4", placement: 0 } - , { value: "Sugestion 5", placement: 1 } - , { value: "Sugestion 6", placement: 2 } - ] - , validation: [] - , id: 19 - }; - - const inputObj3: Input = { - placement: 2 - , description: "Description Question 3 Form 6" - , question: "Question 3 Form 6" - , enabled: true - , type: InputType.SELECT - , sugestions: [ - { value: "Sugestion 1", placement: 0 } - , { value: "Sugestion 2", placement: 1 } - ] - , validation: [ - { type: ValidationType.DEPENDENCY, arguments: ["19", "1"] } - , { type: ValidationType.DEPENDENCY, arguments: ["20", "1"] } - ] - , id: 20 - }; - - const inputObj4: Input = { - placement: 3 - , description: "Description Question 4 Form 6" - , question: "Question 4 Form 6" - , enabled: true - , type: InputType.CHECKBOX - , sugestions: [ - { value: "Sugestion n", placement: 0 } - , { value: "Sugestion n+1", placement: 1 } - , { value: "Sugestion n+2", placement: 2 } - , { value: "Sugestion n+3", placement: 4 } - ] - , validation: [ - { type: ValidationType.SOMECHECKBOX, arguments: [] } - ] - , id: 21 - }; - - const inputObj5: Input = { - placement: 4 - , description: "Description Question 5 Form 6" - , question: "Question 5 Form 6" - , enabled: true - , type: InputType.TEXT - , sugestions: [] - , validation: [ - { type: ValidationType.DEPENDENCY, arguments: ["18", "2"] } - ] - , id: 22 - }; - - const formObj: Form = { - title: "Form Title 6" - , description: "Form Description 6" - , inputs: [ - inputObj1 - , inputObj2 - , inputObj3 - , inputObj4 - , inputObj5 - ] - , id: 6 - }; - - dbhandler.form.write(formObj, (err: Error, formResult: Form) => { + dbhandler.form.write(dbHandlerScenario.formWithInputAnswerSugestions, (err: Error, formResult: Form) => { expect(err).to.be.a("null"); - TestHandler.testForm(formObj, formResult); + TestHandler.testForm(dbHandlerScenario.formWithInputAnswerSugestions, formResult); done(); }); }); it("should insert a form with typeof validation", (done) => { - - const inputObj1: Input = { - placement: 0 - , description: "Description Question 1 Form 7" - , question: "Question 1 form 7" - , enabled: true - , type: InputType.TEXT - , sugestions: [] - , validation: [ - { type: ValidationType.TYPEOF, arguments: ["int"] } - , { type: ValidationType.MAXANSWERS, arguments: ["2"] } - ] - , id: 23 - }; - - const inputObj2: Input = { - placement: 1 - , description: "Description Question 2 Form 7" - , question: "Question 2 form 7" - , enabled: true - , type: InputType.TEXT - , sugestions: [] - , validation: [ - { type: ValidationType.TYPEOF, arguments: ["int"] } - , { type: ValidationType.MAXANSWERS, arguments: ["1"] } - ] - , id: 24 - }; - - const inputObj3: Input = { - placement: 2 - , description: "Description Question 3 Form 7" - , question: "Question 3 form 7" - , enabled: true - , type: InputType.TEXT - , sugestions: [] - , validation: [ - { type: ValidationType.TYPEOF, arguments: ["float"] } - ] - , id: 25 - }; - - const inputObj4: Input = { - placement: 3 - , description: "Description Question 4 Form 7" - , question: "Question 4 form 7" - , enabled: true - , type: InputType.TEXT - , sugestions: [] - , validation: [ - { type: ValidationType.TYPEOF, arguments: ["float"] } - ] - , id: 26 - }; - - const inputObj5: Input = { - placement: 4 - , description: "Description Question 1 Form 8" - , question: "Question 1 form 8" - , enabled: true - , type: InputType.TEXT - , sugestions: [] - , validation: [ - { type: ValidationType.TYPEOF, arguments: ["date"] } - , { type: ValidationType.MAXANSWERS, arguments: ["2"] } - ] - , id: 27 - }; - - const inputObj6: Input = { - placement: 5 - , description: "Description Question 2 Form 8" - , question: "Question 2 form 8" - , enabled: true - , type: InputType.TEXT - , sugestions: [] - , validation: [ - { type: ValidationType.TYPEOF, arguments: ["date"] } - ] - , id: 28 - }; - - const inputObj7: Input = { - placement: 6 - , description: "Description Question 3 Form 8" - , question: "Question 3 form 8" - , enabled: true - , type: InputType.TEXT - , sugestions: [] - , validation: [ - { type: ValidationType.TYPEOF, arguments: ["invalid"] } - , { type: ValidationType.MAXANSWERS, arguments: ["invalid"] } - , { type: ValidationType.DEPENDENCY, arguments: ["28", "invalid"] } - ] - , id: 29 - }; - - const formObj: Form = { - title: "Form Title 7" - , description: "Form Description 7" - , inputs: [ - inputObj1 - , inputObj2 - , inputObj3 - , inputObj4 - , inputObj5 - , inputObj6 - , inputObj7 - ] - , id: 7 - }; - - dbhandler.form.write(formObj, (err: Error, formResult: Form) => { + dbhandler.form.write(dbHandlerScenario.formWithTypeOfValidation, (err: Error, formResult: Form) => { expect(err).to.be.a("null"); - TestHandler.testForm(formObj, formResult); + TestHandler.testForm(dbHandlerScenario.formWithTypeOfValidation, formResult); done(); }); }); it("should insert form answer validation somecheckbox", (done) => { - - const inputAnswersOpt1: InputAnswerOptions = { - id: undefined - , idInput: 18 - , placement: 0 - , value: "true" - }; - - const inputAnswersOpt2: InputAnswerOptions = { - id: undefined - , idInput: 18 - , placement: 1 - , value: "true" - }; - - const inputAnswersOpt3: InputAnswerOptions = { - id: undefined - , idInput: 18 - , placement: 2 - , value: "false" - }; - - const inputAnswersOpt4: InputAnswerOptions = { - id: undefined - , idInput: 19 - , placement: 1 - , value: "true" - }; - - const inputAnswersOpt5: InputAnswerOptions = { - id: undefined - , idInput: 20 - , placement: 1 - , value: "true" - }; - - const inputAnswerOptionsDict: InputAnswerOptionsDict = { - 18: [ - inputAnswersOpt1 - , inputAnswersOpt2 - , inputAnswersOpt3 - ] - , 19: [inputAnswersOpt4] - , 20: [inputAnswersOpt5] - , 21: [] - }; - dbhandler.form.read(6, (error: Error, form: Form) => { const formAnswerOptions: FormAnswerOptions = { form , timestamp: new Date() - , inputsAnswerOptions: inputAnswerOptionsDict + , inputsAnswerOptions: dbHandlerScenario.inputAnswerValidationCheckbox }; const formAnswerObj = new FormAnswer(formAnswerOptions); @@ -1171,4 +437,190 @@ describe("Read and Write on Database", () => { }); }); }); + + it("Should insert a default user in the database", (done) => { + dbhandler.user.write(dbHandlerScenario.toBeInserted, (error: Error, userResult: User) => { + expect(error).to.be.a("null"); + TestHandler.testUser(dbHandlerScenario.toBeInserted, userResult); + done(); + }); + + }); + + it("Should insert an enbale-false user in the database", (done) => { + dbhandler.user.write(dbHandlerScenario.falseEnabled, (error: Error, userResult: User) => { + expect(error).to.be.a("null"); + TestHandler.testUser(dbHandlerScenario.falseEnabled, userResult); + expect(userResult.enabled).to.be.eql(false); + done(); + }); + + }); + + it("Should insert an enabled-null user in the database", (done) => { + dbhandler.user.write(dbHandlerScenario.nullEnabled, (error: Error, userResult: User) => { + expect(error).to.be.a("null"); + TestHandler.testUser(dbHandlerScenario.nullEnabled, userResult); + expect(userResult.enabled).to.be.eql(true); + done(); + }); + }); + + it("Should insert an id-null user in the database", (done) => { + dbhandler.user.write(dbHandlerScenario.nullId, (error: Error, userResult: User) => { + expect(error).to.be.a("null"); + TestHandler.testUser(dbHandlerScenario.nullId, userResult); + expect(userResult.id).to.be.equal(5); + done(); + }); + }); + + it("Should update an user from the database", (done) => { + dbhandler.user.update(dbHandlerScenario.toupdate, dbHandlerScenario.toupdate.id, (err: Error) => { + expect(err).to.be.a("null"); + /** Read the user from DB and tests it with the updated user */ + dbhandler.user.read(2, (error: Error, userResult: User) => { + expect(error).to.be.a("null"); + TestHandler.testUser(dbHandlerScenario.toupdate, userResult); + }); + done(); + }); + }); + + it("Should update an user's enabled from the database", (done) => { + dbhandler.user.update(dbHandlerScenario.updateEnable, dbHandlerScenario.updateEnable.id, (err: Error) => { + expect(err).to.be.a("null"); + /** Read the user from DB and tests it with the updated user */ + dbhandler.user.read(3, (error: Error, userResult: User) => { + expect(error).to.be.a("null"); + TestHandler.testUser(dbHandlerScenario.updateEnable, userResult); + }); + done(); + }); + }); + + it("Should write a form with SubForms", (done) => { + dbhandler.form.write(new Form (dbHandlerScenario.formWithSubForm1), (err: Error, form?: Form) => { + expect(err).to.be.a("null"); + TestHandler.testForm(new Form (dbHandlerScenario.formWithSubForm1), form); + done(); + }); + }); + + it("should update subforms and insert FormUpdate", (done) => { + dbhandler.form.update(new FormUpdate (dbHandlerScenario.formUpdateDeleteAddWithSubForm), (err: Error, form?: Form) => { + expect(err).to.be.a("null"); + done(); + }); + }); + + it("should update change placement with subforms", (done) => { + dbhandler.form.update(new FormUpdate (dbHandlerScenario.formUpdateSwapWithSubForm), (err: Error, form?: Form) => { + expect(err).to.be.a("null"); + done(); + }); + }); + + it("should read an existent form with subform", (done) => { + dbhandler.form.read(8, (err: Error, form: Form) => { + expect(err).to.be.a("null"); + TestHandler.testForm(form, new Form(dbHandlerScenario.updatedFormWithValidSubForm2)); + done(); + }); + }); + + it("should not insert a form with a invalid subForm", (done) => { + dbhandler.form.write(new Form(dbHandlerScenario.formWithInvalidSubForm1), (err: Error, formResult: Form) => { + expect(err).to.be.not.a("null"); + expect(err.message).to.be.equal(ErrorHandler.badIdAmount(0).message); + expect(formResult).to.be.not.a("object"); + done(); + }); + }); + + it("should update form - remove and add a new subform", (done) => { + dbhandler.form.update(new FormUpdate(dbHandlerScenario.formUpdateAddRemoveSubForm), (err: Error) => { + expect(err).to.be.a("null"); + done(); + }); + }); + + it("should read an existent form with subforms", (done) => { + dbhandler.form.read(8, (err: Error, form: Form) => { + TestHandler.testForm(form, new Form(dbHandlerScenario.updatedFormWithValidSubForm2)); + done(); + }); + }); + + it("should not insert a form with himself as subform", (done) => { + dbhandler.form.write(new Form(dbHandlerScenario.formWithInvalidSubForm2), (err: Error, formResult?: Form) => { + expect(err).to.be.not.a("null"); + expect(err.message).to.be.equal(ErrorHandler.badIdAmount(0).message); + expect(formResult).to.be.not.a("object"); + done(); + }); + }); + + it("should not update a form with himself as subform", (done) => { + dbhandler.form.update(new FormUpdate(dbHandlerScenario.formUpdateHimselfAsSubForm), (err: Error) => { + expect(err).to.be.not.a("null"); + expect(err.message).to.be.equal("Found a subform loop"); + done(); + }); + }); + + it("should insert a form with a valid subForm", (done) => { + dbhandler.form.write(new Form(dbHandlerScenario.formWithSubForm2), (err: Error, formResult?: Form) => { + expect(err).to.be.a("null"); + TestHandler.testForm(new Form(dbHandlerScenario.formWithSubForm2), formResult); + done(); + }); + }); + + it("should not update a form with subform loop", (done) => { + dbhandler.form.update(new FormUpdate(dbHandlerScenario.formUpdateWithSubFormLoop), (err: Error) => { + expect(err).to.be.not.a("null"); + expect(err.message).to.be.equal("Found a subform loop"); + done(); + }); + }); + + it("should insert a answer to a form with subform", (done) => { + dbhandler.answer.write(new FormAnswer(dbHandlerScenario.formAnswerWithSubForms), (err: Error, formAnswerResult?: FormAnswer) => { + expect(err).to.be.a("null"); + expect(formAnswerResult.id).to.be.equal(10); + const ids: number[] = [ 26, 34, 33, 35, 36, 18, 19, 20, 21 ]; + let inputAnswerId: number = 0; + for (const key of Object.keys(formAnswerResult.inputAnswers)){ + for (const inputAnswer of formAnswerResult.inputAnswers[parseInt(key, 10)]){ + expect(inputAnswer.id).to.be.equal(ids[inputAnswerId]); + inputAnswerId++; + } + } + done(); + }); + }); + + it("should read an existent form Answer with subForms", (done) => { + dbhandler.answer.read(10, (err: Error, formAnswerResult?: FormAnswer) => { + expect(err).to.be.a("null"); + TestHandler.testFormAnswer(formAnswerResult, new FormAnswer(dbHandlerScenario.formAnswerWithSubForms)); + done(); + }); + }); + + it("Should delete an user from the database", (done) => { + dbhandler.user.delete(2, (err: Error) => { + expect(err).to.be.a("null"); + }); + done(); + }); + + it("Should read a deleted user from the database", (done) => { + dbhandler.user.read(100, (error: Error) => { + expect(error.message).to.be.eql("Bad amount of ids returned: found '0' should be 1"); + }); + done(); + }); + }); diff --git a/src/utils/dbHandler.ts b/src/utils/dbHandler.ts index 6d8217870080a22745d8902a0cee01417c090968..dd6e9f0dc2e52ac5345191b67766ab30be98ab39 100644 --- a/src/utils/dbHandler.ts +++ b/src/utils/dbHandler.ts @@ -20,9 +20,11 @@ */ import { Pool, PoolConfig, QueryResult } from "pg"; +import { User } from "../core/user"; import { AnswerQueryBuilder } from "./answerQueryBuilder"; import { configs } from "./config"; import { FormQueryBuilder } from "./formQueryBuilder"; +import { UserQueryBuilder } from "./userQueryBuilder"; /** * Class of the SGBD from the Form Creator Api perspective. Used to @@ -35,6 +37,8 @@ export class DbHandler { public readonly form: FormQueryBuilder; /** Object used to control FormAnswer operations. */ public readonly answer: AnswerQueryBuilder; + /** OBject used to control User operations */ + public readonly user: UserQueryBuilder; /** Information used to connect with a PostgreSQL database. */ private pool: Pool; @@ -45,5 +49,6 @@ export class DbHandler { this.pool = new Pool(config); this.form = new FormQueryBuilder(this.pool); this.answer = new AnswerQueryBuilder(this.form, this.pool); + this.user = new UserQueryBuilder(this.pool); } } diff --git a/src/utils/diffHandler.spec.ts b/src/utils/diffHandler.spec.ts index 66d4c77b78e7488a3ab51f285e2d57e9e1e1c65e..9d58dc28965728cc9ea12f9dca04c69d0fc04a7d 100644 --- a/src/utils/diffHandler.spec.ts +++ b/src/utils/diffHandler.spec.ts @@ -20,6 +20,7 @@ */ import { expect } from "chai"; +import { diffHandlerScenario } from "../../test/scenario"; import { Form, FormOptions } from "../core/form"; import { FormUpdate, FormUpdateOptions } from "../core/formUpdate"; import { Input, InputOptions, Validation } from "../core/input"; @@ -30,1086 +31,63 @@ import { TestHandler } from "./testHandler"; describe("Diff Handler", () => { it("should return a valid formUpdate remove type", (done) => { - const newInputObj1: Input = { - placement: 0 - , description: "Description Question 1 Form 1" - , question: "Question 1 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [] - , id: 1 - }; - const newInputObj2: Input = { - placement: 1 - , description: "Description Question 2 Form 1" - , question: "Question 2 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: ValidationType.MAXCHAR, arguments: ["10"] } - , { type: ValidationType.MINCHAR, arguments: ["2"] } - ] - , id: 3 - }; - const newFormObj: Form = { - id: 1 - , title: "Form Title 1" - , description: "Form Description 1" - , inputs: [ - newInputObj1 - , newInputObj2 - ] - }; - - const oldInputObj1: Input = { - placement: 0 - , description: "Description Question 1 Form 1" - , question: "Question 1 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [] - , id: 1 - }; - const oldInputObj2: Input = { - placement: 1 - , description: "Description Question 2 Form 1" - , question: "Question 2 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: ValidationType.MAXCHAR, arguments: ["10"] } - , { type: ValidationType.MINCHAR, arguments: ["2"] } - ] - , id: 3 - }; - const oldInputObj3: Input = { - placement: 2 - , description: "Description Question 3 Form 1" - , question: "Question 3 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: ValidationType.REGEX, arguments: ["\\d{5}-\\d{3}"] } - , { type: ValidationType.MANDATORY, arguments: [] } - ] - , id: 2 - }; - const oldFormObj: Form = { - id: 1 - , title: "Form Title 1" - , description: "Form Description 1" - , inputs: [ - oldInputObj1 - , oldInputObj2 - , oldInputObj3 - ] - }; - - const resFormUpdate = DiffHandler.diff(newFormObj, oldFormObj); - - const expInputUpdate: InputUpdate = { - input: oldInputObj3 - , inputOperation: UpdateType.REMOVE - , value: null - }; - const expFormUpdate: FormUpdate = { - form: newFormObj - , updateDate: new Date() - , inputUpdates: [ expInputUpdate ] - }; - TestHandler.testFormUpdate(resFormUpdate, expFormUpdate); + const resFormUpdate = DiffHandler.diff(diffHandlerScenario.newFormObjREMOVE, diffHandlerScenario.oldFormObj); + TestHandler.testFormUpdate(resFormUpdate, diffHandlerScenario.expFormUpdateREMOVE); done(); }); it("should return a valid formUpdate add type", (done) => { - const newInputObj1: Input = { - placement: 0 - , description: "Description Question 1 Form 1" - , question: "Question 1 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [] - , id: 1 - - }; - const newInputObj2: Input = { - placement: 1 - , description: "Description Question 2 Form 1" - , question: "Question 2 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: ValidationType.MAXCHAR, arguments: ["10"] } - , { type: ValidationType.MINCHAR, arguments: ["2"] } - ] - , id: 3 - }; - const newInputObj3: Input = { - placement: 2 - , description: "Description Question 3 Form 1" - , question: "Question 3 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: ValidationType.REGEX, arguments: ["\\d{5}-\\d{3}"] } - , { type: ValidationType.MANDATORY, arguments: [] } - ] - , id: 2 - }; - const newInputObj4: Input = { - placement: 3 - , description: "Description Question 4 Form 1" - , question: "Question 4 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [] - , id: undefined - }; - - const newFormObj: Form = { - id: 1 - , title: "Form Title 1" - , description: "Form Description 1" - , inputs: [ - newInputObj1 - , newInputObj2 - , newInputObj3 - , newInputObj4 - ] - }; - - const oldInputObj1: Input = { - placement: 0 - , description: "Description Question 1 Form 1" - , question: "Question 1 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [] - , id: 1 - }; - const oldInputObj2: Input = { - placement: 1 - , description: "Description Question 2 Form 1" - , question: "Question 2 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: ValidationType.MAXCHAR, arguments: ["10"] } - , { type: ValidationType.MINCHAR, arguments: ["2"] } - ] - , id: 3 - }; - const oldInputObj3: Input = { - placement: 2 - , description: "Description Question 3 Form 1" - , question: "Question 3 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: ValidationType.REGEX, arguments: ["\\d{5}-\\d{3}"] } - , { type: ValidationType.MANDATORY, arguments: [] } - ] - , id: 2 - }; - const oldFormObj: Form = { - id: 1 - , title: "Form Title 1" - , description: "Form Description 1" - , inputs: [ - oldInputObj1 - , oldInputObj2 - , oldInputObj3 - ] - }; + const resFormUpdate = DiffHandler.diff(diffHandlerScenario.newFormObjADD, diffHandlerScenario.oldFormObj); - const resFormUpdate = DiffHandler.diff(newFormObj, oldFormObj); - - const expInputUpdate: InputUpdate = { - input: newInputObj4 - , inputOperation: UpdateType.ADD - , value: null - }; - const expFormUpdate: FormUpdate = { - form: newFormObj - , updateDate: new Date() - , inputUpdates: [ expInputUpdate ] - }; - TestHandler.testFormUpdate(resFormUpdate, expFormUpdate); + TestHandler.testFormUpdate(resFormUpdate, diffHandlerScenario.expFormUpdateADD); done(); }); it("should return a valid formUpdate swap type", (done) => { - const newInputObj1: Input = { - placement: 1 - , description: "Description Question 1 Form 1" - , question: "Question 1 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [] - , id: 1 - - }; - const newInputObj2: Input = { - placement: 0 - , description: "Description Question 2 Form 1" - , question: "Question 2 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: ValidationType.MAXCHAR, arguments: ["10"] } - , { type: ValidationType.MINCHAR, arguments: ["2"] } - ] - , id: 3 - }; - const newInputObj3: Input = { - placement: 2 - , description: "Description Question 3 Form 1" - , question: "Question 3 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: ValidationType.REGEX, arguments: ["\\d{5}-\\d{3}"] } - , { type: ValidationType.MANDATORY, arguments: [] } - ] - , id: 2 - }; - const newFormObj: Form = { - id: 1 - , title: "Form Title 1" - , description: "Form Description 1" - , inputs: [ - newInputObj1 - , newInputObj2 - , newInputObj3 - ] - }; - - const oldInputObj1: Input = { - placement: 0 - , description: "Description Question 1 Form 1" - , question: "Question 1 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [] - , id: 1 - }; - const oldInputObj2: Input = { - placement: 1 - , description: "Description Question 2 Form 1" - , question: "Question 2 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: ValidationType.MAXCHAR, arguments: ["10"] } - , { type: ValidationType.MINCHAR, arguments: ["2"] } - ] - , id: 3 - }; - const oldInputObj3: Input = { - placement: 2 - , description: "Description Question 3 Form 1" - , question: "Question 3 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: ValidationType.REGEX, arguments: ["\\d{5}-\\d{3}"] } - , { type: ValidationType.MANDATORY, arguments: [] } - ] - , id: 2 - }; - const oldFormObj: Form = { - id: 1 - , title: "Form Title 1" - , description: "Form Description 1" - , inputs: [ - oldInputObj1 - , oldInputObj2 - , oldInputObj3 - ] - }; - - const resFormUpdate = DiffHandler.diff(newFormObj, oldFormObj); - - const expInputUpdate1: InputUpdate = { - input: newInputObj1 - , inputOperation: UpdateType.SWAP - , value: "" + 0 - }; + const resFormUpdate = DiffHandler.diff(diffHandlerScenario.newFormObjSWAP, diffHandlerScenario.oldFormObj); - const expInputUpdate2: InputUpdate = { - input: newInputObj2 - , inputOperation: UpdateType.SWAP - , value: "" + 1 - }; - - const expFormUpdate: FormUpdate = { - form: newFormObj - , updateDate: new Date() - , inputUpdates: [ - expInputUpdate1 - , expInputUpdate2 - ] - }; - TestHandler.testFormUpdate(resFormUpdate, expFormUpdate); + TestHandler.testFormUpdate(resFormUpdate, diffHandlerScenario.expFormUpdateSWAP); done(); }); it("should return a valid formUpdate remove and add all", (done) => { - const newInputObj1: Input = { - placement: 0 - , description: "Description Question 1 Form 1" - , question: "Question 1 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [] - , id: undefined - }; - const newInputObj2: Input = { - placement: 1 - , description: "Description Question 2 Form 1" - , question: "Question 2 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: ValidationType.MAXCHAR, arguments: ["10"] } - , { type: ValidationType.MINCHAR, arguments: ["2"] } - ] - , id: undefined - }; - const newInputObj3: Input = { - placement: 2 - , description: "Description Question 3 Form 1" - , question: "Question 3 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: ValidationType.REGEX, arguments: ["\\d{5}-\\d{3}"] } - , { type: ValidationType.MANDATORY, arguments: [] } - ] - , id: undefined - }; - - const newFormObj: Form = { - id: 1 - , title: "Form Title 1" - , description: "Form Description 1" - , inputs: [ - newInputObj1 - , newInputObj2 - , newInputObj3 - ] - }; - - const oldInputObj1: Input = { - placement: 0 - , description: "Description Question 1 Form 1" - , question: "Question 1 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [] - , id: 1 - }; - const oldInputObj2: Input = { - placement: 1 - , description: "Description Question 2 Form 1" - , question: "Question 2 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: ValidationType.MAXCHAR, arguments: ["10"] } - , { type: ValidationType.MINCHAR, arguments: ["2"] } - ] - , id: 3 - }; - const oldInputObj3: Input = { - placement: 2 - , description: "Description Question 3 Form 1" - , question: "Question 3 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: ValidationType.REGEX, arguments: ["\\d{5}-\\d{3}"] } - , { type: ValidationType.MANDATORY, arguments: [] } - ] - , id: 2 - }; - const oldFormObj: Form = { - id: 1 - , title: "Form Title 1" - , description: "Form Description 1" - , inputs: [ - oldInputObj1 - , oldInputObj2 - , oldInputObj3 - ] - }; - - const resFormUpdate = DiffHandler.diff(newFormObj, oldFormObj); - - const expInputUpdate1: InputUpdate = { - input: oldInputObj1 - , inputOperation: UpdateType.REMOVE - , value: null - }; - const expInputUpdate2: InputUpdate = { - input: oldInputObj3 - , inputOperation: UpdateType.REMOVE - , value: null - }; - const expInputUpdate3: InputUpdate = { - input: oldInputObj2 - , inputOperation: UpdateType.REMOVE - , value: null - }; - const expInputUpdate4: InputUpdate = { - input: newInputObj1 - , inputOperation: UpdateType.ADD - , value: null - }; - const expInputUpdate5: InputUpdate = { - input: newInputObj2 - , inputOperation: UpdateType.ADD - , value: null - }; - const expInputUpdate6: InputUpdate = { - input: newInputObj3 - , inputOperation: UpdateType.ADD - , value: null - }; - - const expFormUpdate: FormUpdate = { - form: newFormObj - , updateDate: new Date() - , inputUpdates: [ - expInputUpdate1 - , expInputUpdate2 - , expInputUpdate3 - , expInputUpdate4 - , expInputUpdate5 - , expInputUpdate6 - ] - }; + const resFormUpdate = DiffHandler.diff(diffHandlerScenario.newFormObjREMOVEandADD, diffHandlerScenario.oldFormObj); - TestHandler.testFormUpdate(resFormUpdate, expFormUpdate); + TestHandler.testFormUpdate(resFormUpdate, diffHandlerScenario.expFormUpdateREMOVEandADD); done(); }); it("should return a valid formUpdate all operations", (done) => { - const newInputObj1: Input = { - placement: 1 - , description: "Description Question 1 Form 1" - , question: "Question 1 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [] - , id: 1 - }; - const newInputObj2: Input = { - placement: 0 - , description: "Description Question 2 Form 1" - , question: "Question 2 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: ValidationType.MAXCHAR, arguments: ["10"] } - , { type: ValidationType.MINCHAR, arguments: ["2"] } - ] - , id: 2 - }; - const newInputObj3: Input = { - placement: 2 - , description: "Description Question 3 Form 1" - , question: "Question 3 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: ValidationType.REGEX, arguments: ["\\d{5}-\\d{3}"] } - , { type: ValidationType.MANDATORY, arguments: [] } - ] - , id: 3 - }; - const newInputObj4: Input = { - placement: 3 - , description: "Description Question 4 Form 1" - , question: "Question 4 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [] - , id: undefined - }; - const newFormObj: Form = { - id: 1 - , title: "Form Title 1" - , description: "Form Description 1" - , inputs: [ - newInputObj1 - , newInputObj2 - , newInputObj3 - , newInputObj4 - ] - }; + const resFormUpdate = DiffHandler.diff(diffHandlerScenario.newFormObjALL, diffHandlerScenario.oldFormAll); - const oldInputObj1: Input = { - placement: 0 - , description: "Description Question 1 Form 1" - , question: "Question 1 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [] - , id: 1 - }; - const oldInputObj2: Input = { - placement: 1 - , description: "Description Question 2 Form 1" - , question: "Question 2 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: ValidationType.MAXCHAR, arguments: ["10"] } - , { type: ValidationType.MINCHAR, arguments: ["2"] } - ] - , id: 2 - }; - const oldInputObj3: Input = { - placement: 2 - , description: "Description Question 4 Form 1" - , question: "Question 4 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: ValidationType.MAXCHAR, arguments: ["10"] } - , { type: ValidationType.MINCHAR, arguments: ["2"] } - ] - , id: 4 - }; - const oldFormObj: Form = { - id: 1 - , title: "Form Title 1" - , description: "Form Description 1" - , inputs: [ - oldInputObj1 - , oldInputObj2 - , oldInputObj3 - ] - }; - - const resFormUpdate = DiffHandler.diff(newFormObj, oldFormObj); - - const expInputUpdate1: InputUpdate = { - input: newInputObj1 - , inputOperation: UpdateType.SWAP - , value: "" + 0 - }; - const expInputUpdate2: InputUpdate = { - input: newInputObj2 - , inputOperation: UpdateType.SWAP - , value: "" + 1 - }; - const expInputUpdate3: InputUpdate = { - input: newInputObj3 - , inputOperation: UpdateType.REENABLED - , value: null - }; - const expInputUpdate4: InputUpdate = { - input: oldInputObj3 - , inputOperation: UpdateType.REMOVE - , value: null - }; - const expInputUpdate5: InputUpdate = { - input: newInputObj4 - , inputOperation: UpdateType.ADD - , value: null - }; - - const expFormUpdate: FormUpdate = { - form: newFormObj - , updateDate: new Date() - , inputUpdates: [ - expInputUpdate1 - , expInputUpdate2 - , expInputUpdate3 - , expInputUpdate4 - , expInputUpdate5 - ] - }; - - TestHandler.testFormUpdate(resFormUpdate, expFormUpdate); - done(); - }); - it("should return a valid formUpdate remove and add all", (done) => { - const newInputObj1: Input = { - placement: 0 - , description: "Description Question 1 Form 1" - , question: "Question 1 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [] - , id: undefined - }; - const newInputObj2: Input = { - placement: 1 - , description: "Description Question 2 Form 1" - , question: "Question 2 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: ValidationType.MAXCHAR, arguments: ["10"] } - , { type: ValidationType.MINCHAR, arguments: ["2"] } - ] - , id: undefined - }; - const newInputObj3: Input = { - placement: 2 - , description: "Description Question 3 Form 1" - , question: "Question 3 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: ValidationType.REGEX, arguments: ["\\d{5}-\\d{3}"] } - , { type: ValidationType.MANDATORY, arguments: [] } - ] - , id: undefined - }; - - const newFormObj: Form = { - id: 1 - , title: "Form Title 1" - , description: "Form Description 1" - , inputs: [ - newInputObj1 - , newInputObj2 - , newInputObj3 - ] - }; - - const oldInputObj1: Input = { - placement: 0 - , description: "Description Question 1 Form 1" - , question: "Question 1 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [] - , id: 1 - }; - const oldInputObj2: Input = { - placement: 1 - , description: "Description Question 2 Form 1" - , question: "Question 2 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: ValidationType.MAXCHAR, arguments: ["10"] } - , { type: ValidationType.MINCHAR, arguments: ["2"] } - ] - , id: 3 - }; - const oldInputObj3: Input = { - placement: 2 - , description: "Description Question 3 Form 1" - , question: "Question 3 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: ValidationType.REGEX, arguments: ["\\d{5}-\\d{3}"] } - , { type: ValidationType.MANDATORY, arguments: [] } - ] - , id: 2 - }; - const oldFormObj: Form = { - id: 1 - , title: "Form Title 1" - , description: "Form Description 1" - , inputs: [ - oldInputObj1 - , oldInputObj2 - , oldInputObj3 - ] - }; - - const resFormUpdate = DiffHandler.diff(newFormObj, oldFormObj); - - const expInputUpdate1: InputUpdate = { - input: oldInputObj1 - , inputOperation: UpdateType.REMOVE - , value: null - }; - const expInputUpdate2: InputUpdate = { - input: oldInputObj3 - , inputOperation: UpdateType.REMOVE - , value: null - }; - const expInputUpdate3: InputUpdate = { - input: oldInputObj2 - , inputOperation: UpdateType.REMOVE - , value: null - }; - const expInputUpdate4: InputUpdate = { - input: newInputObj1 - , inputOperation: UpdateType.ADD - , value: null - }; - const expInputUpdate5: InputUpdate = { - input: newInputObj2 - , inputOperation: UpdateType.ADD - , value: null - }; - const expInputUpdate6: InputUpdate = { - input: newInputObj3 - , inputOperation: UpdateType.ADD - , value: null - }; - - const expFormUpdate: FormUpdate = { - form: newFormObj - , updateDate: new Date() - , inputUpdates: [ - expInputUpdate1 - , expInputUpdate2 - , expInputUpdate3 - , expInputUpdate4 - , expInputUpdate5 - , expInputUpdate6 - ] - }; - TestHandler.testFormUpdate(resFormUpdate, expFormUpdate); + TestHandler.testFormUpdate(resFormUpdate, diffHandlerScenario.expFormUpdateALL); done(); }); it("should return a valid formUpdate restore inputs", (done) => { - const newInputObj1: Input = { - placement: 0 - , description: "Description Question 1 Form 1" - , question: "Question 1 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [] - , id: 1 - }; - const newInputObj2: Input = { - placement: 2 - , description: "Description Question 2 Form 1" - , question: "Question 2 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: ValidationType.MAXCHAR, arguments: ["10"] } - , { type: ValidationType.MINCHAR, arguments: ["2"] } - ] - , id: 2 - }; - const newInputObj3: Input = { - placement: 1 - , description: "Description Question 3 Form 1" - , question: "Question 3 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: ValidationType.REGEX, arguments: ["\\d{5}-\\d{3}"] } - , { type: ValidationType.MANDATORY, arguments: [] } - ] - , id: 3 - }; - const newFormObj: Form = { - id: 1 - , title: "Form Title 1" - , description: "Form Description 1" - , inputs: [ - newInputObj1 - , newInputObj2 - , newInputObj3 - ] - }; - - const oldInputObj1: Input = { - placement: 0 - , description: "Description Question 1 Form 1" - , question: "Question 1 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [] - , id: 1 - }; - const oldInputObj2: Input = { - placement: 1 - , description: "Description Question 2 Form 1" - , question: "Question 3 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: ValidationType.MAXCHAR, arguments: ["10"] } - , { type: ValidationType.MINCHAR, arguments: ["2"] } - ] - , id: 3 - }; - const oldFormObj: Form = { - id: 1 - , title: "Form Title 1" - , description: "Form Description 1" - , inputs: [ - oldInputObj1 - , oldInputObj2 - ] - }; - - const resFormUpdate = DiffHandler.diff(newFormObj, oldFormObj); + const resFormUpdate = DiffHandler.diff(diffHandlerScenario.newFormObjRESTORE, diffHandlerScenario.oldFormRestore); - const expInputUpdate1: InputUpdate = { - input: newInputObj2 - , inputOperation: UpdateType.REENABLED - , value: null - }; - const expFormUpdate: FormUpdate = { - form: newFormObj - , updateDate: new Date() - , inputUpdates: [ - expInputUpdate1 - ] - }; - - TestHandler.testFormUpdate(resFormUpdate, expFormUpdate); + TestHandler.testFormUpdate(resFormUpdate, diffHandlerScenario.expFormUpdateRESTORE); done(); }); it("should return a valid formUpdate create a new Form", (done) => { - const newInputObj1: Input = { - placement: 1 - , description: "Description Question 1 Form 1" - , question: "Question 1 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [] - , id: null - }; - const newInputObj2: Input = { - placement: 0 - , description: "Description Question 2 Form 1" - , question: "Question 2 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: ValidationType.MAXCHAR, arguments: ["10"] } - , { type: ValidationType.MINCHAR, arguments: ["2"] } - ] - , id: null - }; - const newInputObj3: Input = { - placement: 2 - , description: "Description Question 3 Form 1" - , question: "Question 3 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: ValidationType.REGEX, arguments: ["\\d{5}-\\d{3}"] } - , { type: ValidationType.MANDATORY, arguments: [] } - ] - , id: null - }; - const newFormObj: Form = { - id: 1 - , title: "Form Title 1" - , description: "Form Description 1" - , inputs: [ - newInputObj1 - , newInputObj2 - , newInputObj3 - ] - }; - - const oldFormObj: Form = { - id: 1 - , title: "Form Title 1" - , description: "Form Description 1" - , inputs: [] - }; + const resFormUpdate = DiffHandler.diff(diffHandlerScenario.newFormObjCREATE, diffHandlerScenario.FormObjEmpty); - const resFormUpdate = DiffHandler.diff(newFormObj, oldFormObj); - - const expInputUpdate1: InputUpdate = { - input: newInputObj1 - , inputOperation: UpdateType.ADD - , value: null - }; - const expInputUpdate2: InputUpdate = { - input: newInputObj2 - , inputOperation: UpdateType.ADD - , value: null - }; - const expInputUpdate3: InputUpdate = { - input: newInputObj3 - , inputOperation: UpdateType.ADD - , value: null - }; - - const expFormUpdate: FormUpdate = { - form: newFormObj - , updateDate: new Date() - , inputUpdates: [ - expInputUpdate1 - , expInputUpdate2 - , expInputUpdate3 - ] - }; - TestHandler.testFormUpdate(resFormUpdate, expFormUpdate); + TestHandler.testFormUpdate(resFormUpdate, diffHandlerScenario.expFormUpdateCREATE); done(); }); it("should return a valid formUpdate remove all inputs", (done) => { - - const newFormObj: Form = { - id: 1 - , title: "Form Title 1" - , description: "Form Description 1" - , inputs: [] - }; - - const oldInputObj1: Input = { - placement: 0 - , description: "Description Question 1 Form 1" - , question: "Question 1 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [] - , id: 1 - }; - const oldInputObj2: Input = { - placement: 1 - , description: "Description Question 2 Form 1" - , question: "Question 2 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: ValidationType.MAXCHAR, arguments: ["10"] } - , { type: ValidationType.MINCHAR, arguments: ["2"] } - ] - , id: 3 - }; - const oldInputObj3: Input = { - placement: 2 - , description: "Description Question 3 Form 1" - , question: "Question 3 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: ValidationType.REGEX, arguments: ["\\d{5}-\\d{3}"] } - , { type: ValidationType.MANDATORY, arguments: [] } - ] - , id: 2 - }; - const oldFormObj: Form = { - id: 1 - , title: "Form Title 1" - , description: "Form Description 1" - , inputs: [ - oldInputObj1 - , oldInputObj2 - , oldInputObj3 - ] - }; - - const resFormUpdate = DiffHandler.diff(newFormObj, oldFormObj); - - const expInputUpdate1: InputUpdate = { - input: oldInputObj1 - , inputOperation: UpdateType.REMOVE - , value: null - }; - const expInputUpdate2: InputUpdate = { - input: oldInputObj3 - , inputOperation: UpdateType.REMOVE - , value: null - }; - const expInputUpdate3: InputUpdate = { - input: oldInputObj2 - , inputOperation: UpdateType.REMOVE - , value: null - }; - - const expFormUpdate: FormUpdate = { - form: newFormObj - , updateDate: new Date() - , inputUpdates: [ - expInputUpdate1 - , expInputUpdate2 - , expInputUpdate3 - ] - }; - - TestHandler.testFormUpdate(resFormUpdate, expFormUpdate); + const resFormUpdate = DiffHandler.diff(diffHandlerScenario.FormObjEmpty, diffHandlerScenario.oldFormObj); + TestHandler.testFormUpdate(resFormUpdate, diffHandlerScenario.expFormUpdateREMOVEALL); done(); }); it("should return a valid formUpdate wich changes the title", (done) => { - const newInputObj1: Input = { - placement: 0 - , description: "Description Question 1 Form 1" - , question: "Question 1 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [] - , id: 1 - }; - const newInputObj2: Input = { - placement: 1 - , description: "Description Question 2 Form 1" - , question: "Question 2 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: ValidationType.MAXCHAR, arguments: ["10"] } - , { type: ValidationType.MINCHAR, arguments: ["2"] } - ] - , id: 3 - }; - const newFormObj: Form = { - id: 1 - , title: "Title 1" - , description: "Description 1" - , inputs: [ - newInputObj1 - , newInputObj2 - ] - }; - - const oldInputObj1: Input = { - placement: 0 - , description: "Description Question 1 Form 1" - , question: "Question 1 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [] - , id: 1 - }; - const oldInputObj2: Input = { - placement: 1 - , description: "Description Question 2 Form 1" - , question: "Question 2 Form 1" - , enabled: true - , type: InputType.TEXT - , validation: [ - { type: ValidationType.MAXCHAR, arguments: ["10"] } - , { type: ValidationType.MINCHAR, arguments: ["2"] } - ] - , id: 3 - }; - const oldFormObj: Form = { - id: 1 - , title: "Form Title 1" - , description: "Form Description 1" - , inputs: [ - oldInputObj1 - , oldInputObj2 - ] - }; - - const resFormUpdate = DiffHandler.diff(newFormObj, oldFormObj); + const resFormUpdate = DiffHandler.diff(diffHandlerScenario.newFormObjWrongTitle, diffHandlerScenario.odlFormObjCorrectTitle); - const expFormUpdate: FormUpdate = { - form: newFormObj - , updateDate: new Date() - , changed: true - , inputUpdates: [] - }; - TestHandler.testFormUpdate(resFormUpdate, expFormUpdate); + TestHandler.testFormUpdate(resFormUpdate, diffHandlerScenario.expFormUpdateTITLE); done(); }); }); diff --git a/src/utils/diffHandler.ts b/src/utils/diffHandler.ts index c033ce7e9ea62656016245657caa9f8b97fa31f4..eda68f970fd1fcf6168a0d007045803a5bf5959f 100644 --- a/src/utils/diffHandler.ts +++ b/src/utils/diffHandler.ts @@ -87,6 +87,7 @@ export class DiffHandler { while ((j < inputsToAdd.length)) { formUpdate.inputUpdates.push(DiffHandler.addInput(inputsToAdd[j])); j++; + } return formUpdate; } diff --git a/src/utils/enumHandler.spec.ts b/src/utils/enumHandler.spec.ts index b992999db09082db0648ac1b0aa089aa9d7458c3..557827629954a88cc411c22d57ceff78cb4fe6d9 100644 --- a/src/utils/enumHandler.spec.ts +++ b/src/utils/enumHandler.spec.ts @@ -20,143 +20,78 @@ */ import { expect } from "chai"; +import { enumHandlerScenario } from "../../test/scenario"; import { EnumHandler, InputType, UpdateType, ValidationType } from "./enumHandler"; - describe("Enum Handler", () => { it("should stringify UpdateType ", () => { - - const updateNone = EnumHandler.stringifyUpdateType(UpdateType.NONE); - const updateAdd = EnumHandler.stringifyUpdateType(UpdateType.ADD); - const updateRemove = EnumHandler.stringifyUpdateType(UpdateType.REMOVE); - const updateSwap = EnumHandler.stringifyUpdateType(UpdateType.SWAP); - const updateReenabled = EnumHandler.stringifyUpdateType(UpdateType.REENABLED); - - expect(updateNone).to.be.equal(""); - expect(updateAdd).to.be.equal("add"); - expect(updateRemove).to.be.equal("remove"); - expect(updateSwap).to.be.equal("swap"); - expect(updateReenabled).to.be.equal("reenabled"); + expect(enumHandlerScenario.sUpdateNone).to.be.equal(""); + expect(enumHandlerScenario.sUpdateAdd).to.be.equal("add"); + expect(enumHandlerScenario.sUpdateRemove).to.be.equal("remove"); + expect(enumHandlerScenario.sUpdateSwap).to.be.equal("swap"); + expect(enumHandlerScenario.sUpdateReenabled).to.be.equal("reenabled"); }); it("should parse string to UpdateType", () => { - const updateAdd = EnumHandler.parseUpdateType("add"); - const updateAddCapitalLetters = EnumHandler.parseUpdateType("ADD"); - const updateRemove = EnumHandler.parseUpdateType("remove"); - const updateRemoveCapitalLetters = EnumHandler.parseUpdateType("REMOVE"); - const updateSwap = EnumHandler.parseUpdateType("swap"); - const updateSwapCapitalLetters = EnumHandler.parseUpdateType("SWAP"); - const updateReenabled = EnumHandler.parseUpdateType("reenabled"); - const updateReenabledCapitalLetters = EnumHandler.parseUpdateType("REENABLED"); - const updateNone = EnumHandler.parseUpdateType(""); - const updateFOOL = EnumHandler.parseUpdateType("fool"); - - expect(updateAdd).to.be.equal(UpdateType.ADD); - expect(updateAddCapitalLetters).to.be.equal(UpdateType.ADD); - expect(updateRemove).to.be.equal(UpdateType.REMOVE); - expect(updateRemoveCapitalLetters).to.be.equal(UpdateType.REMOVE); - expect(updateSwap).to.be.equal(UpdateType.SWAP); - expect(updateSwapCapitalLetters).to.be.equal(UpdateType.SWAP); - expect(updateNone).to.be.equal(UpdateType.NONE); - expect(updateFOOL).to.be.equal(UpdateType.NONE); + expect(enumHandlerScenario.pUpdateAdd).to.be.equal(UpdateType.ADD); + expect(enumHandlerScenario.pUpdateAddCapitalLetters).to.be.equal(UpdateType.ADD); + expect(enumHandlerScenario.pUpdateRemove).to.be.equal(UpdateType.REMOVE); + expect(enumHandlerScenario.pUpdateRemoveCapitalLetters).to.be.equal(UpdateType.REMOVE); + expect(enumHandlerScenario.pUpdateSwap).to.be.equal(UpdateType.SWAP); + expect(enumHandlerScenario.pUpdateSwapCapitalLetters).to.be.equal(UpdateType.SWAP); + expect(enumHandlerScenario.pUpdateNone).to.be.equal(UpdateType.NONE); + expect(enumHandlerScenario.pUpdateFOOL).to.be.equal(UpdateType.NONE); }); it("should stringify InputType ", () => { - - const inputNone = EnumHandler.stringifyInputType(InputType.NONE); - const inputText = EnumHandler.stringifyInputType(InputType.TEXT); - const inputRadio = EnumHandler.stringifyInputType(InputType.RADIO); - const inputCheckbox = EnumHandler.stringifyInputType(InputType.CHECKBOX); - - expect(inputNone).to.be.equal(""); - expect(inputText).to.be.equal("text"); - expect(inputCheckbox).to.be.equal("checkbox"); - expect(inputRadio).to.be.equal("radio"); + expect(enumHandlerScenario.sInputNone).to.be.equal(""); + expect(enumHandlerScenario.sInputText).to.be.equal("text"); + expect(enumHandlerScenario.sInputCheckbox).to.be.equal("checkbox"); + expect(enumHandlerScenario.sInputRadio).to.be.equal("radio"); }); it("should parse string to InputType", () => { - const inputNone = EnumHandler.parseInputType(""); - const inputText = EnumHandler.parseInputType("text"); - const inputTextCapitalLetters = EnumHandler.parseInputType("TEXT"); - const inputRadio = EnumHandler.parseInputType("radio"); - const inputRadioCapitalLetters = EnumHandler.parseInputType("RADIO"); - const inputCheckbox = EnumHandler.parseInputType("checkbox"); - const inputCheckboxCapitalLetters = EnumHandler.parseInputType("CHECKBOX"); - const inputFOOL = EnumHandler.parseInputType("fool"); - const inputSelect = EnumHandler.parseInputType("select"); - const inputSelectCapitalLetters = EnumHandler.parseInputType("SELECT"); - - expect(inputText).to.be.equal(InputType.TEXT); - expect(inputTextCapitalLetters).to.be.equal(InputType.TEXT); - expect(inputNone).to.be.equal(InputType.NONE); - expect(inputFOOL).to.be.equal(InputType.NONE); - expect(inputCheckbox).to.be.equal(InputType.CHECKBOX); - expect(inputCheckboxCapitalLetters).to.be.equal(InputType.CHECKBOX); - expect(inputRadio).to.be.equal(InputType.RADIO); - expect(inputRadioCapitalLetters).to.be.equal(InputType.RADIO); - expect(inputSelect).to.be.equal(InputType.SELECT); - expect(inputSelectCapitalLetters).to.be.equal(InputType.SELECT); + expect(enumHandlerScenario.pInputText).to.be.equal(InputType.TEXT); + expect(enumHandlerScenario.pInputTextCapitalLetters).to.be.equal(InputType.TEXT); + expect(enumHandlerScenario.pInputNone).to.be.equal(InputType.NONE); + expect(enumHandlerScenario.pInputFOOL).to.be.equal(InputType.NONE); + expect(enumHandlerScenario.pInputCheckbox).to.be.equal(InputType.CHECKBOX); + expect(enumHandlerScenario.pInputCheckboxCapitalLetters).to.be.equal(InputType.CHECKBOX); + expect(enumHandlerScenario.pInputRadio).to.be.equal(InputType.RADIO); + expect(enumHandlerScenario.pInputRadioCapitalLetters).to.be.equal(InputType.RADIO); + expect(enumHandlerScenario.pInputSelect).to.be.equal(InputType.SELECT); + expect(enumHandlerScenario.pInputSelectCapitalLetters).to.be.equal(InputType.SELECT); }); it("should stringify ValidationType ", () => { - - const validationRegex = EnumHandler.stringifyValidationType(ValidationType.REGEX); - const validationMandatory = EnumHandler.stringifyValidationType(ValidationType.MANDATORY); - const validationMaxChar = EnumHandler.stringifyValidationType(ValidationType.MAXCHAR); - const validationMinChar = EnumHandler.stringifyValidationType(ValidationType.MINCHAR); - const validationTypeOf = EnumHandler.stringifyValidationType(ValidationType.TYPEOF); - const validationSomeCheckbox = EnumHandler.stringifyValidationType(ValidationType.SOMECHECKBOX); - const validationMaxAnswers = EnumHandler.stringifyValidationType(ValidationType.MAXANSWERS); - const validationNone = EnumHandler.stringifyValidationType(ValidationType.NONE); - - expect(validationNone).to.be.equal(""); - expect(validationRegex).to.be.equal("regex"); - expect(validationMandatory).to.be.equal("mandatory"); - expect(validationMaxChar).to.be.equal("maxchar"); - expect(validationMinChar).to.be.equal("minchar"); - expect(validationTypeOf).to.be.equal("typeof"); - expect(validationSomeCheckbox).to.be.equal("somecheckbox"); - expect(validationMaxAnswers).to.be.equal("maxanswers"); + expect(enumHandlerScenario.sValidationNone).to.be.equal(""); + expect(enumHandlerScenario.sValidationRegex).to.be.equal("regex"); + expect(enumHandlerScenario.sValidationMandatory).to.be.equal("mandatory"); + expect(enumHandlerScenario.sValidationMaxChar).to.be.equal("maxchar"); + expect(enumHandlerScenario.sValidationMinChar).to.be.equal("minchar"); + expect(enumHandlerScenario.sValidationTypeOf).to.be.equal("typeof"); + expect(enumHandlerScenario.sValidationSomeCheckbox).to.be.equal("somecheckbox"); + expect(enumHandlerScenario.sValidationMaxAnswers).to.be.equal("maxanswers"); }); it("should parse string to ValidationType", () => { - const validationRegex = EnumHandler.parseValidationType("regex"); - const validationRegexCapitalized = EnumHandler.parseValidationType("REGEX"); - const validationMandatory = EnumHandler.parseValidationType("mandatory"); - const validationMandatoryCapitalized = EnumHandler.parseValidationType("MANDATORY"); - const validationMaxChar = EnumHandler.parseValidationType("maxchar"); - const validationMaxCharyCapitalized = EnumHandler.parseValidationType("MAXCHAR"); - const validationMinChar = EnumHandler.parseValidationType("minchar"); - const validationMinCharyCapitalized = EnumHandler.parseValidationType("MINCHAR"); - const validationTypeOf = EnumHandler.parseValidationType("typeof"); - const validationTypeOfCapitalized = EnumHandler.parseValidationType("TYPEOF"); - const validationSomeCheckbox = EnumHandler.parseValidationType("somecheckbox"); - const validationSomeCheckboxCapitalized = EnumHandler.parseValidationType("SOMECHECKBOX"); - const validationMaxAnswers = EnumHandler.parseValidationType("maxanswers"); - const validationMaxAnswersCapitalized = EnumHandler.parseValidationType("MAXANSWERS"); - const validationDependency = EnumHandler.parseValidationType("dependency"); - const validationDependencyCapitalized = EnumHandler.parseValidationType("DEPENDENCY"); - const validationNone = EnumHandler.parseValidationType(""); - const validatioFOOL = EnumHandler.parseValidationType("fool"); - - expect(validationRegex).to.be.equal(ValidationType.REGEX); - expect(validationRegexCapitalized).to.be.equal(ValidationType.REGEX); - expect(validationMandatory).to.be.equal(ValidationType.MANDATORY); - expect(validationMandatoryCapitalized).to.be.equal(ValidationType.MANDATORY); - expect(validationMaxChar).to.be.equal(ValidationType.MAXCHAR); - expect(validationMaxCharyCapitalized).to.be.equal(ValidationType.MAXCHAR); - expect(validationMinChar).to.be.equal(ValidationType.MINCHAR); - expect(validationMinCharyCapitalized).to.be.equal(ValidationType.MINCHAR); - expect(validationTypeOf).to.be.equal(ValidationType.TYPEOF); - expect(validationTypeOfCapitalized).to.be.equal(ValidationType.TYPEOF); - expect(validationSomeCheckbox).to.be.equal(ValidationType.SOMECHECKBOX); - expect(validationSomeCheckboxCapitalized).to.be.equal(ValidationType.SOMECHECKBOX); - expect(validationMaxAnswers).to.be.equal(ValidationType.MAXANSWERS); - expect(validationMaxAnswersCapitalized).to.be.equal(ValidationType.MAXANSWERS); - expect(validationDependency).to.be.equal(ValidationType.DEPENDENCY); - expect(validationDependencyCapitalized).to.be.equal(ValidationType.DEPENDENCY); - expect(validationNone).to.be.equal(ValidationType.NONE); - expect(validatioFOOL).to.be.equal(ValidationType.NONE); + expect(enumHandlerScenario.pValidationRegex).to.be.equal(ValidationType.REGEX); + expect(enumHandlerScenario.pValidationRegexCapitalized).to.be.equal(ValidationType.REGEX); + expect(enumHandlerScenario.pValidationMandatory).to.be.equal(ValidationType.MANDATORY); + expect(enumHandlerScenario.pValidationMandatoryCapitalized).to.be.equal(ValidationType.MANDATORY); + expect(enumHandlerScenario.pValidationMaxChar).to.be.equal(ValidationType.MAXCHAR); + expect(enumHandlerScenario.pValidationMaxCharyCapitalized).to.be.equal(ValidationType.MAXCHAR); + expect(enumHandlerScenario.pValidationMinChar).to.be.equal(ValidationType.MINCHAR); + expect(enumHandlerScenario.pValidationMinCharyCapitalized).to.be.equal(ValidationType.MINCHAR); + expect(enumHandlerScenario.pValidationTypeOf).to.be.equal(ValidationType.TYPEOF); + expect(enumHandlerScenario.pValidationTypeOfCapitalized).to.be.equal(ValidationType.TYPEOF); + expect(enumHandlerScenario.pValidationSomeCheckbox).to.be.equal(ValidationType.SOMECHECKBOX); + expect(enumHandlerScenario.pValidationSomeCheckboxCapitalized).to.be.equal(ValidationType.SOMECHECKBOX); + expect(enumHandlerScenario.pValidationMaxAnswers).to.be.equal(ValidationType.MAXANSWERS); + expect(enumHandlerScenario.pValidationMaxAnswersCapitalized).to.be.equal(ValidationType.MAXANSWERS); + expect(enumHandlerScenario.pValidationDependency).to.be.equal(ValidationType.DEPENDENCY); + expect(enumHandlerScenario.pValidationDependencyCapitalized).to.be.equal(ValidationType.DEPENDENCY); + expect(enumHandlerScenario.pValidationNone).to.be.equal(ValidationType.NONE); + expect(enumHandlerScenario.pValidatioFOOL).to.be.equal(ValidationType.NONE); }); - }); diff --git a/src/utils/enumHandler.ts b/src/utils/enumHandler.ts index b7d2600f797f8a4eb2ae23a2c76d2944faf3bf68..5035452407a1a797f9cb72218345fb3e3062e439 100644 --- a/src/utils/enumHandler.ts +++ b/src/utils/enumHandler.ts @@ -28,6 +28,7 @@ export enum InputType { CHECKBOX, RADIO, SELECT, + SUBFORM, NONE } @@ -83,6 +84,8 @@ export class EnumHandler { return "radio"; case InputType.SELECT: return "select"; + case InputType.SUBFORM: + return "subform"; default: return ""; } @@ -104,6 +107,8 @@ export class EnumHandler { return InputType.RADIO; case "select": return InputType.SELECT; + case "subform": + return InputType.SUBFORM; default: return InputType.NONE; } diff --git a/src/utils/formQueryBuilder.ts b/src/utils/formQueryBuilder.ts index 8d906b3fdb329a507cd97be12c324ca05797ec09..7bf7f7416c9a0e15d564864b532f280b2aa8a0bb 100644 --- a/src/utils/formQueryBuilder.ts +++ b/src/utils/formQueryBuilder.ts @@ -25,6 +25,7 @@ import { Form, FormOptions } from "../core/form"; import { FormUpdate, FormUpdateOptions } from "../core/formUpdate"; import { Input, InputOptions, Sugestion, Validation } from "../core/input"; import { InputUpdate, InputUpdateOptions } from "../core/inputUpdate"; +import { SubForm } from "../core/subForm"; import { EnumHandler, InputType, UpdateType, ValidationType } from "./enumHandler"; import { ErrorHandler} from "./errorHandler"; import { OptHandler } from "./optHandler"; @@ -52,13 +53,12 @@ export class FormQueryBuilder extends QueryBuilder { } /** - * Asynchronously list all forms using a transaction. - * @param cb - Callback function which contains the data read. + * Asynchronously lists all forms from an user. + * @param userId - User ID who owns the form list. + * @param cb - Callback function which contains information about method's execution. * @param cb.err - Error information when the method fails. - * @param cb.form - list of form or a empty list if there is no form on database. */ - public list(cb: (err: Error, result?: Form[]) => void) { - + public list(userId: number, cb: (err: Error, forms?: Form[]) => void) { waterfall([ (callback: (err: Error, result?: QueryResult) => void) => { this.begin((error: Error, results?: QueryResult) => { @@ -67,7 +67,7 @@ export class FormQueryBuilder extends QueryBuilder { }, (callback: (err: Error, result?: Form[]) => void) => { - this.executeListForms((error: Error, results?: Form[]) => { + this.executeListForms(userId, (error: Error, results?: Form[]) => { callback(error, results); }); }, @@ -89,16 +89,20 @@ export class FormQueryBuilder extends QueryBuilder { } /** - * Asynchronously get all forms from database without transactions. - * @param cb - Callback function which contains the data read. + * Asynchronously lists all forms from an user in the database. + * @param userId - User ID who owns the forms. + * @param cb - Callback function. * @param cb.err - Error information when the method fails. - * @param cb.form - list of form or a empty list if there is no form on database. */ - private executeListForms(cb: (err: Error, result?: Form[]) => void) { - const queryString: string = "SELECT id, title, description FROM form;"; + private executeListForms(userId: number, cb: (err: Error, forms?: Form[]) => void) { + const queryString: string = "SELECT t1.id,t1.title,t1.description FROM form t1 \ + INNER JOIN form_owner t2 ON (t1.id=t2.id_form \ + AND t2.id_user=$1);"; const query: QueryOptions = { query: queryString - , parameters: [] + , parameters: [ + userId + ] }; const forms: Form[] = []; @@ -124,7 +128,6 @@ export class FormQueryBuilder extends QueryBuilder { cb(null, forms); }); - } /** @@ -173,7 +176,7 @@ export class FormQueryBuilder extends QueryBuilder { * @param cb.err - Error information when the method fails. * @param cb.form - Form or null if form not exists. */ - private readController(id: number, cb: (err: Error, form?: Form) => void) { + public readController(id: number, cb: (err: Error, form?: Form) => void) { waterfall([ (callback: (err: Error, result?: Form) => void) => { this.executeReadForm(id, (error: Error, result: QueryResult) => { @@ -264,6 +267,7 @@ export class FormQueryBuilder extends QueryBuilder { , enabled: i["enabled"] , validation: [] , sugestions: [] + , subForm: undefined }; inputArrayTmp.push(new Input(inputTmp)); } @@ -307,6 +311,66 @@ export class FormQueryBuilder extends QueryBuilder { callback(null, form); }); + }, + (form: Form, callback: (err: Error, form?: Form) => void) => { + const subFormInputs: Input[] = form.inputs.filter((obj) => obj.type === InputType.SUBFORM); + const inputs: Input[] = form.inputs.filter((obj) => obj.type !== InputType.SUBFORM); + + waterfall([ + (outerCallback: (er: Error, newSubFormInputs?: Input[]) => void) => { + const newSubFormInputs: Input[] = []; + eachSeries(subFormInputs, (input, innerCallback) => { + this.executeReadSubForm(input.id, (error: Error, result?: QueryResult) => { + for (const i of result.rows) { + const inputTmp: Input = { + id: input.id + , placement: input.placement + , description: input.description + , question: input.question + , enabled: input.enabled + , type: input.type + , validation: input.validation + , sugestions: input.sugestions + , subForm: new SubForm({ + id: i.id + , inputId: i.id_input + , contentFormId: i.id_content_form + }) + }; + newSubFormInputs.push(inputTmp); + } + innerCallback(error); + }); + }, (e) => { + if (e) { + outerCallback(e); + return; + } + outerCallback(null, newSubFormInputs); + }); + }, + (newSubFormInputs: Input[], anotherCallback: (er: Error, newForm: Form) => void) => { + let inputsTmp = inputs; + for (const i of newSubFormInputs) { + inputsTmp.push(i); + } + inputsTmp = Sorter.sortByPlacement(inputsTmp); + + const formTmp: Form = new Form({ + id: form.id + , title: form.title + , description: form.description + , inputs: inputsTmp + }); + anotherCallback(null, formTmp); + } + ], (er, formTmp?: Form) => { + if (er) { + callback(er); + } else { + callback(null, formTmp); + } + }); } ], (err, result?: Form) => { if (err) { @@ -317,6 +381,25 @@ export class FormQueryBuilder extends QueryBuilder { }); } + /** + * Asynchronously read a SubForm without transactions. + * @param id - Input identifier that a subForm belongs to. + * @param cb - Callback function which contains the data read. + * @param cb.err - Error information when the method fails. + * @param cb.subForm - SubForm or null if subForm not exists. + */ + private executeReadSubForm(id: number, cb: (err: Error, subForm?: QueryResult) => void) { + const queryString: string = "SELECT id, id_input, id_content_form FROM sub_form WHERE id_input=$1;"; + const query: QueryOptions = { + query: queryString + , parameters: [id] + }; + + this.executeQuery(query, (err: Error, result?: QueryResult) => { + cb(err, result); + }); + } + /** * Asynchronously read a form without transactions. * @param id - Form identifier to be founded. @@ -341,7 +424,7 @@ export class FormQueryBuilder extends QueryBuilder { * @param id - Form identifier which inputs are linked to. * @param cb - Callback function which contains the data read. * @param cb.err - Error information when the method fails. - * @param cb.form - Form or null if form not exists. + * @param cb.result - inputs or null if inputs not exists. */ private executeReadInput(id: number, cb: (err: Error, result?: QueryResult) => void) { const queryString: string = "SELECT input.id, id_form, placement, input_type, question, \ @@ -363,7 +446,7 @@ export class FormQueryBuilder extends QueryBuilder { * @param id - Form identifier which validations from inputs are linked to. * @param cb - Callback function which contains the data read. * @param cb.err - Error information when the method fails. - * @param cb.form - Form or null if form not exists. + * @param cb.result - Validations or null if validations not exists. */ private executeReadValidation(id: number, cb: (err: Error, result?: QueryResult) => void) { const queryString: string = "SELECT input_validation.id, id_input, validation_type FROM form f \ @@ -385,7 +468,7 @@ export class FormQueryBuilder extends QueryBuilder { * @param id - Form identifier which arguments from validatation are linked to. * @param cb - Callback function which contains the data read. * @param cb.err - Error information when the method fails. - * @param cb.form - Form or null if form not exists. + * @param cb.result - Arguments or null if arguments not exists. */ private executeReadArgument(id: number, cb: (err: Error, result?: QueryResult) => void) { const queryString: string = "SELECT input_validation_argument.id, id_input_validation AS id_validation, \ @@ -406,10 +489,10 @@ export class FormQueryBuilder extends QueryBuilder { /** * Asynchronously read sugestions from database without transactions. - * @param id - Form identifier wich arguments from inputs are linked to. + * @param id - Form identifier wich sugestions from inputs are linked to. * @param cb - Callback function which contains the data read. * @param cb.err - Error information when the method fails. - * @param cb.form - Form or null if form not exists. + * @param cb.result - Sugestions or null if sugestions not exists. */ private executeReadSugestion(id: number, cb: (err: Error, result?: QueryResult) => void) { const queryString: string = "SELECT input_sugestion.id, id_input, value, input_sugestion.placement \ @@ -482,13 +565,18 @@ export class FormQueryBuilder extends QueryBuilder { }); }, (formId: number, callback: (err: Error, result?: number) => void) => { - this.writeInputController(formId, form.inputs, (error: Error) => { - callback(error, formId); + eachSeries(form.inputs, (input, innerCallback) => { + this.writeInputController(formId, input, (error: Error) => { + innerCallback(error); + }); + }, (e) => { + callback(e, formId); }); - } - ], (err, id: number) => { + } + ], (err, id?: number) => { if (err) { cb(err); + return; } cb(null, id); }); @@ -498,44 +586,89 @@ export class FormQueryBuilder extends QueryBuilder { /** * Asynchronously write a input on database without transactions. * @param formId - Form identifier which inputs are linked to. - * @param inputs - A list of inputs to be inserted. + * @param input - A input to be inserted. * @param cb - Callback function which contains informations about method's execution. * @param cb.err - Error information when the method fails. */ - private writeInputController(formId: number, inputs: Input[], cb: (err: Error) => void) { - eachSeries(inputs, (input, outerCallback) => { + private writeInputController(formId: number, input: Input, cb: (err: Error, inputId?: number) => void) { + waterfall([ + (callback: (err: Error, result?: number) => void) => { + this.executeWriteInput(formId, input, callback); + }, + (inputId: number, callback: (err: Error, resultInputId?: number) => void) => { + this.writeValidationController(inputId, input.validation, (error: Error) => { + callback(error, inputId); + }); + }, + (inputId: number, callback: (err: Error, resultInputId?: number) => void) => { + this.writeSugestionController(inputId, input.sugestions, (error: Error) => { + callback(error, inputId); + }); + }, + (inputId: number, callback: (err: Error, resultInputId?: number) => void) => { + this.writeSubFormController(formId, inputId, input, (error: Error) => { + callback(error, inputId); + }); + } + ], (err, id?: number) => { + cb(err, id); + return; + }); + } + + /** + * Asynchronously write a write a SubForm on database and update a input with his identifier. + * @param formId - Form identifier which SubForms are linked to. + * @param inputId - Input identifier which SubForms are linked to. + * @param input - Input that contains the subform to be inserted. + * @param cb - Callback function which contains informations about method's execution. + * @param cb.err - Error information when the method fails. + */ + private writeSubFormController(formId: number, inputId: number, input: Input, cb: (err: Error) => void) { + if (input.type !== InputType.SUBFORM) { + cb(null); + return; + } else { waterfall([ - (callback: (err: Error, result?: number) => void) => { - this.executeWriteInput(formId, input, (error: Error, resultInputId?: number) => { - if (error) { - callback(error); - return; - } - callback(null, resultInputId); - }); - }, - (inputId: number, callback: (err: Error, resultInputId?: number) => void) => { - this.writeValidationController(inputId, input.validation, (error: Error) => { - callback(error, inputId); - }); - }, - (inputId: number, callback: (err: Error) => void) => { - this.writeSugestionController(inputId, input.sugestions, (error: Error) => { + (callback: (err: Error) => void) => { + this.executeVerifyForm(input.subForm.contentFormId, (error: Error) => { callback(error); }); + }, + (callback: (err: Error) => void) => { + this.executeWriteSubForm(inputId, input.subForm.contentFormId, callback); } ], (err) => { - if (err) { - outerCallback(err); - return; - } - outerCallback(null); + cb(err); + return; }); - }, (e) => { - cb(e); - }); + } } + /** + * Verify if a Form exists in the database. + * @param id - Form identifier to be verified. + * @param cb - Callback function which contains informations about method's execution. + * @param cb.err - Error information when the method fails. + */ + private executeVerifyForm(id: number, cb: (err: Error) => void) { + const queryString: string = "SELECT id FROM form where id=$1;"; + const query: QueryOptions = { + query: queryString + , parameters: [ id ] + }; + + this.executeQuery(query, (error: Error, result?: QueryResult) => { + + if (result.rowCount !== 1) { + cb(ErrorHandler.badIdAmount(result.rowCount)); + return; + } + + cb(error); + return; + }); + } /** * Asynchronously write a validation on database without transactions. * @param inputId - Input identifier which validations are linked to. @@ -603,6 +736,35 @@ export class FormQueryBuilder extends QueryBuilder { }); } + /** + * Asynchronously insert a form on database without transactions. + * @param inputId - Input identifier to be linked with a subForm. + * @param contentFormId - A Form identifier to be linked with the SubForm object. + * @param cb - Callback function which contains informations about method's execution. + * @param cb.err - Error information when the method fails. + * @param cb.result - Form identifier or null if any error occurs. + */ + private executeWriteSubForm(inputId: number, contentFormId: number, cb: (err: Error, result?: number) => void) { + const queryString: string = "INSERT INTO sub_form (id_input, id_content_form) \ + VALUES ($1, $2)"; + const query: QueryOptions = { + query: queryString + , parameters: [ + inputId + , contentFormId + ] + }; + + this.executeQuery(query, (err: Error, result?: QueryResult) => { + if (err) { + cb(err); + return; + } + cb(null); + return; + }); + } + /** * Asynchronously insert a input on database without transactions. * @param formId - Form identifier which input are linked to. @@ -635,6 +797,7 @@ export class FormQueryBuilder extends QueryBuilder { /** * Asynchronously insert a validation on database without transactions. * @param inputId - Input identifier which validations are linked to. + * @param validation - Validation to be inserted. * @param cb - Callback function which contains informations about method's execution. * @param cb.err - Error information when the method fails. * @param cb.result - Validation identifier or null if any error occurs. @@ -684,6 +847,7 @@ export class FormQueryBuilder extends QueryBuilder { /** * Asynchronously insert a sugestion on database without transactions. * @param inputId - Input identifier which sugestion are linked to. + * @param sugestion - Sugestions to be inserted. * @param cb - Callback function which contains informations about method's execution. * @param cb.err - Error information when the method fails. * @param cb.result - Form identifier or null if any error occurs. @@ -707,7 +871,7 @@ export class FormQueryBuilder extends QueryBuilder { /** * Asynchronously update a form on database. - * @param form - Form to be updated. + * @param formUpdate - FormUpdate object that contains the Form to be updated. * @param cb - Callback function which contains information about method's execution. * @param cb.err - Error information when the method fails. */ @@ -819,7 +983,46 @@ export class FormQueryBuilder extends QueryBuilder { } /** - * Asynchronously update a list of inputs on database. + * Check if exists a loop on subForms. + * @param fathers - Form identifiers ids of subForms that has been already readed. + * @param input - Input with a subForm to be checked. + * @param cb - Callback function which contains information about method's execution. + * @param cb.err - Error information when the method fails. + */ + private checkSubFormLoop(fathers: number[], input: Input, cb: (err: Error) => void) { + waterfall([ + (callback: (error: Error) => void) => { + let error: Error = null; + for (const i of fathers) { + if (input.subForm.contentFormId === i) { + error = new Error("Found a subform loop"); + } + } + callback(error); + }, + (callback: (error: Error, formResult?: Form) => void) => { + this.readController(input.subForm.contentFormId, callback); + }, + (form: Form, callback: (err: Error) => void) => { + eachSeries(form.inputs, (j, innerCallback) => { + if (j.type === InputType.SUBFORM) { + fathers.push(input.subForm.contentFormId); + this.checkSubFormLoop(fathers, j, innerCallback); + } else { + innerCallback(null); + } + }, (err) => { + callback(err); + }); + } + ], (error) => { + cb(error); + return; + }); + } + + /** + * Asynchronously update a list of inputs on database without transactions. * @param formId - Form identifier which update are linked to. * @param inputUpdates - InputUpdate array which contains the update information. * @param cb - Callback function which contains information about method's execution. @@ -833,23 +1036,38 @@ export class FormQueryBuilder extends QueryBuilder { eachSeries(inputUpdates, (inputUpdate, callback) => { switch (inputUpdate.inputOperation) { case UpdateType.ADD: { - this.executeWriteInput(formId, inputUpdate.input, (err: Error, id: number) => { - - if (err) { - callback(err); - return; + waterfall([ + (innerCallback: (err: Error) => void) => { + const vector: number[] = [formId]; + if (inputUpdate.input.type === InputType.SUBFORM) { + this.checkSubFormLoop(vector, inputUpdate.input, innerCallback); + } else { + innerCallback(null); + } + }, + (innerCallback: (err: Error) => void) => { + this.writeInputController(formId, inputUpdate.input, (err: Error, id: number) => { + + if (err) { + innerCallback(err); + return; + } + + const inputOpt: InputOptions = inputUpdate.input; + inputOpt.id = id; + + const inputUpdateOpt: InputUpdateOptions = { + input: inputOpt + , inputOperation: UpdateType.ADD + , value: null + }; + + inputUpdatesTmp.push(new InputUpdate(inputUpdateOpt)); + innerCallback(null); + }); } - - const inputOpt: InputOptions = inputUpdate.input; - inputOpt.id = id; - - const inputUpdateOpt: InputUpdateOptions = { - input: inputOpt - , inputOperation: UpdateType.ADD - , value: null - }; - inputUpdatesTmp.push(new InputUpdate(inputUpdateOpt)); - callback(null); + ], (err) => { + callback(err); }); break; } @@ -910,7 +1128,7 @@ export class FormQueryBuilder extends QueryBuilder { } /** - * Asynchronously update form's fields on database. + * Asynchronously update form's fields on database without transactions. * @param value - A string to be inserted in the database. * @param id - The form id that should be updated. * @param field - The field on database that should be updated. @@ -932,7 +1150,7 @@ export class FormQueryBuilder extends QueryBuilder { } /** - * Asynchronously update a input field on database. + * Asynchronously update a input field on database without transactions. * @param value - A number to be inserted in the database. * @param id - The input id that should be updated. * @param field - The field on database that should be updated. @@ -954,7 +1172,7 @@ export class FormQueryBuilder extends QueryBuilder { } /** - * Asynchronously insert a formUpdate on database. + * Asynchronously insert a formUpdate on database without transactions. * @param formUpdate - Form Update to be inserted. * @param cb - Callback function. * @param cb.err - Error information when the method fails. @@ -983,8 +1201,9 @@ export class FormQueryBuilder extends QueryBuilder { } /** - * Asynchronously insert a inputUpdate on database. - * @param inputUpdate - Input Update to be inserted. + * Asynchronously insert a inputUpdate on database without transactions. + * @param idFormUpdate - FormUpdate identifier to link with the InputUpdate. + * @param inputUpdate - InputUpdate to be inserted. * @param cb - Callback function. * @param cb.err - Error information when the method fails. */ diff --git a/src/utils/optHandler.spec.ts b/src/utils/optHandler.spec.ts index 5170308e67bcbe00450de8d431b5dece573fced4..de4b6b45b9c4b5dbb35bae27cc9d9542bd767adb 100644 --- a/src/utils/optHandler.spec.ts +++ b/src/utils/optHandler.spec.ts @@ -19,12 +19,14 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ import { expect } from "chai"; +import { optHandlerScenario } from "../../test/scenario"; import { Form, FormOptions } from "../core/form"; import { FormAnswer, FormAnswerOptions } from "../core/formAnswer"; import { FormUpdate, FormUpdateOptions } from "../core/formUpdate"; import { Input, InputOptions, Validation } from "../core/input"; import { InputAnswer, InputAnswerDict, InputAnswerOptions, InputAnswerOptionsDict } from "../core/inputAnswer"; import { InputUpdate, InputUpdateOptions } from "../core/inputUpdate"; +import { SubForm, SubFormOptions } from "../core/subForm"; import { InputType, UpdateType, ValidationType } from "./enumHandler"; import { ErrorHandler } from "./errorHandler"; import { OptHandler } from "./optHandler"; @@ -32,51 +34,10 @@ import { OptHandler } from "./optHandler"; describe("Options Handler", () => { it("should get error when missing Form title", () => { - const inputObj1: InputOptions = { - placement: 0 - , description: "Description Question 1 Form 1" - , question: "Question 1 Form 1" - , type: InputType.TEXT - , validation: [] - , id: 1 - }; - - const inputObj2: InputOptions = { - placement: 1 - , description: "Description Question 2 Form 1" - , question: "Question 2 Form 1" - , type: InputType.TEXT - , validation: [ - { type: ValidationType.MAXCHAR, arguments: ["10"] } - , { type: ValidationType.MINCHAR, arguments: ["2"] } - ] - , id: 3 - }; - - const inputObj3: InputOptions = { - placement: 2 - , description: "Description Question 3 Form 1" - , question: "Question 3 Form 1" - , type: InputType.TEXT - , validation: [ - { type: ValidationType.REGEX, arguments: ["\\d{5}-\\d{3}"] } - , { type: ValidationType.MANDATORY, arguments: [] } - ] - , id: 2 - }; - const formObj: any = { - id: 1 - , description: "Form Description 1" - , inputs: [ - OptHandler.input(inputObj1) - , OptHandler.input(inputObj2) - , OptHandler.input(inputObj3) - ] - }; let formTmp: Form; - try{ - formTmp = new Form ( OptHandler.form(formObj)); - }catch (e){ + try { + formTmp = new Form ( OptHandler.form(optHandlerScenario.formMissingTitle)); + } catch (e) { expect(e).to.be.not.a("null"); expect(e.message).to.be.equal(ErrorHandler.notFound("Form title").message); expect(formTmp).to.be.a("undefined"); @@ -84,661 +45,164 @@ describe("Options Handler", () => { }); it("should get error when missing Form description", () => { - const inputObj1: InputOptions = { - placement: 0 - , description: "Description Question 1 Form 1" - , question: "Question 1 Form 1" - , type: InputType.TEXT - , validation: [] - , id: 1 - }; - - const inputObj2: InputOptions = { - placement: 1 - , description: "Description Question 2 Form 1" - , question: "Question 2 Form 1" - , type: InputType.TEXT - , validation: [ - { type: ValidationType.MAXCHAR, arguments: ["10"] } - , { type: ValidationType.MINCHAR, arguments: ["2"] } - ] - , id: 3 - }; - - const inputObj3: InputOptions = { - placement: 2 - , description: "Description Question 3 Form 1" - , question: "Question 3 Form 1" - , type: InputType.TEXT - , validation: [ - { type: ValidationType.REGEX, arguments: ["\\d{5}-\\d{3}"] } - , { type: ValidationType.MANDATORY, arguments: [] } - ] - , id: 2 - }; - const formObj: any = { - id: 1 - , title: "Form Title 1" - , inputs: [ - OptHandler.input(inputObj1) - , OptHandler.input(inputObj2) - , OptHandler.input(inputObj3) - ] - }; let formTmp: Form; - - try{ - formTmp = new Form ( OptHandler.form(formObj)); - }catch (e){ + try { + formTmp = new Form ( OptHandler.form(optHandlerScenario.formMissingDescription)); + } catch (e) { expect(e).to.be.not.a("null"); expect(e.message).to.be.equal(ErrorHandler.notFound("Form description").message); expect(formTmp).to.be.a("undefined"); } - }); it("should get error when missing Form inputs", () => { - const formObj: any = { - id: 1 - , title: "Form Title 1" - , description: "Form Description 1" - }; let formTmp: Form; - - try{ - formTmp = new Form ( OptHandler.form(formObj)); - - }catch (e){ + try { + formTmp = new Form ( OptHandler.form(optHandlerScenario.formMissingInputs)); + } catch (e) { expect(e).to.be.not.a("null"); expect(e.message).to.be.equal(ErrorHandler.notFound("Form inputs").message); expect(formTmp).to.be.a("undefined"); } - }); it("should get error when Form inputs is not an array", () => { - const formObj: any = { - id: 1 - , title: "Form Title 1" - , description: "Form Description 1" - }; let formTmp: Form; - - try{ - formTmp = new Form ( OptHandler.form(formObj)); - - }catch (e){ + try { + formTmp = new Form ( OptHandler.form(optHandlerScenario.formInputNotAnArray)); + } catch (e) { expect(e).to.be.not.a("null"); expect(e.message).to.be.equal(ErrorHandler.notFound("Form inputs").message); expect(formTmp).to.be.a("undefined"); } - }); it("should get error when missing Input placement", () => { - const inputObj: any = { - description: "Description Question 1 Form 1" - , question: "Question 1 Form 1" - , type: InputType.TEXT - , validation: [] - , inputs: "Fool" - , id: 1 - }; let inputTmp: Input; - - try{ - inputTmp = new Input(OptHandler.input(inputObj)); - - }catch (e){ + try { + inputTmp = new Input(OptHandler.input(optHandlerScenario.inputMissingPlacement)); + } catch (e) { expect(e).to.be.not.a("null"); expect(e.message).to.be.equal(ErrorHandler.notFound("Input placement").message); expect(inputTmp).to.be.a("undefined"); } - }); it("should get error when missing Input description", () => { - const inputObj: any = { - placement: 0 - , question: "Question 1 Form 1" - , type: InputType.TEXT - , validation: [] - , id: 1 - }; let inputTmp: Input; - - try{ - inputTmp = new Input(OptHandler.input(inputObj)); - - }catch (e){ + try { + inputTmp = new Input(OptHandler.input(optHandlerScenario.inputMissingDescription)); + } catch (e) { expect(e).to.be.not.a("null"); expect(e.message).to.be.equal(ErrorHandler.notFound("Input description").message); expect(inputTmp).to.be.a("undefined"); } - }); it("should get error when missing Input question ", () => { - const inputObj: any = { - placement: 0 - , description: "Description Question 1 Form 1" - , type: InputType.TEXT - , validation: [] - , id: 1 - }; let inputTmp: Input; - - try{ - inputTmp = new Input(OptHandler.input(inputObj)); - - }catch (e){ + try { + inputTmp = new Input(OptHandler.input(optHandlerScenario.inputMissingQuestion)); + } catch (e) { expect(e).to.be.not.a("null"); expect(e.message).to.be.equal(ErrorHandler.notFound("Input question").message); expect(inputTmp).to.be.a("undefined"); } - }); it("should get error when missing Input type", () => { - const inputObj: any = { - placement: 0 - , description: "Description Question 1 Form 1" - , question: "Question 1 Form 1" - , validation: [] - , id: 1 - }; let inputTmp: Input; - - try{ - inputTmp = new Input(OptHandler.input(inputObj)); - - }catch (e){ + try { + inputTmp = new Input(OptHandler.input(optHandlerScenario.inputMissingType)); + } catch (e) { expect(e).to.be.not.a("null"); expect(e.message).to.be.equal(ErrorHandler.notFound("Input type").message); expect(inputTmp).to.be.a("undefined"); } - }); it("should get error when missing Input validation", () => { - const inputObj: any = { - placement: 0 - , description: "Description Question 1 Form 1" - , question: "Question 1 Form 1" - , type: InputType.TEXT - , id: 1 - }; let inputTmp: Input; - - try{ - inputTmp = new Input(OptHandler.input(inputObj)); - - }catch (e){ + try { + inputTmp = new Input(OptHandler.input(optHandlerScenario.inputMissingValidation)); + } catch (e) { expect(e).to.be.not.a("null"); expect(e.message).to.be.equal(ErrorHandler.notFound("Input validation").message); expect(inputTmp).to.be.a("undefined"); } - }); it("should get error when Input validation is not an array", () => { - const inputObj: any = { - placement: 0 - , description: "Description Question 1 Form 1" - , question: "Question 1 Form 1" - , type: InputType.TEXT - , validation: "fool" - , id: 1 - }; let inputTmp: Input; - - try{ - inputTmp = new Input(OptHandler.input(inputObj)); - - }catch (e){ + try { + inputTmp = new Input(OptHandler.input(optHandlerScenario.inputValidationNotAnArray)); + } catch (e) { expect(e).to.be.not.a("null"); expect(e.message).to.be.equal(ErrorHandler.notFound("Input validation").message); expect(inputTmp).to.be.a("undefined"); } - }); it("should get error when FormAnswer has no form associated", () => { - const inputAnswersOpt1: InputAnswerOptions = { - id: 1 - , idInput: null - , placement: 0 - , value: "Answer 1 to Question 1 Form 1" - }; - const inputAnswersOpt2: InputAnswerOptions = { - id: 2 - , idInput: null - , placement: 0 - , value: "Answer 1 to Question 2 Form" - }; - const inputAnswersOpt3: InputAnswerOptions = { - id: 3 - , idInput: null - , placement: 0 - , value: "Answer 1 to Question 3 Form" - }; - const inputAnswersOpt4: InputAnswerOptions = { - id: 4 - , idInput: null - , placement: 1 - , value: "Answer 2 to Question 3 Form" - }; - - const inputAnswerOptionsDict: InputAnswerOptionsDict = {1: [inputAnswersOpt1], 2: [inputAnswersOpt2], 3: [inputAnswersOpt3, inputAnswersOpt4]}; - const data: Date = new Date(2019, 5, 15); - const formAnswerOptions: any = { - id: 1 - , timestamp: data - , inputsAnswerOptions: inputAnswerOptionsDict - }; let formAnswerTmp: FormAnswer; - - try{ - formAnswerTmp = new FormAnswer(OptHandler.formAnswer(formAnswerOptions)); - }catch (e){ + try { + formAnswerTmp = new FormAnswer(OptHandler.formAnswer(optHandlerScenario.noFormAssociated)); + } catch (e) { expect(e).to.be.not.a("null"); expect(e.message).to.be.equal(ErrorHandler.notFound("Form").message); expect(formAnswerTmp).to.be.a("undefined"); } - }); it("should get error when FormAnswer has a form associated but not type form", () => { - - const inputAnswersOpt1: InputAnswerOptions = { - id: 1 - , idInput: null - , placement: 0 - , value: "Answer 1 to Question 1 Form 1" - }; - const inputAnswersOpt2: InputAnswerOptions = { - id: 2 - , idInput: null - , placement: 0 - , value: "Answer 1 to Question 2 Form" - }; - const inputAnswersOpt3: InputAnswerOptions = { - id: 3 - , idInput: null - , placement: 0 - , value: "Answer 1 to Question 3 Form" - }; - const inputAnswersOpt4: InputAnswerOptions = { - id: 4 - , idInput: null - , placement: 1 - , value: "Answer 2 to Question 3 Form" - }; - - const inputAnswerOptionsDict: InputAnswerOptionsDict = {1: [inputAnswersOpt1], 2: [inputAnswersOpt2], 3: [inputAnswersOpt3, inputAnswersOpt4]}; - const data: Date = new Date(2019, 5, 15); - const formAnswerOptions: any = { - id: 1 - , form: inputAnswersOpt4 - , timestamp: data - , inputsAnswerOptions: inputAnswerOptionsDict - }; let formAnswerTmp: FormAnswer; - - try{ - formAnswerTmp = new FormAnswer(OptHandler.formAnswer(formAnswerOptions)); - }catch (e){ + try { + formAnswerTmp = new FormAnswer(OptHandler.formAnswer(optHandlerScenario.noFormType)); + } catch (e) { expect(e).to.be.not.a("null"); expect(e.message).to.be.equal(ErrorHandler.notFound("Form").message); expect(formAnswerTmp).to.be.a("undefined"); } - }); it("should get error when FormAnswer has no date associated", () => { - - const inputObj1: InputOptions = { - placement: 0 - , description: "Description Question 1 Form 1" - , question: "Question 1 Form 1" - , type: InputType.TEXT - , validation: [] - , id: 1 - }; - - const inputObj2: InputOptions = { - placement: 1 - , description: "Description Question 2 Form 1" - , question: "Question 2 Form 1" - , type: InputType.TEXT - , validation: [ - { type: ValidationType.MAXCHAR, arguments: ["10"] } - , { type: ValidationType.MINCHAR, arguments: ["2"] } - ] - , id: 3 - }; - - const inputObj3: InputOptions = { - placement: 2 - , description: "Description Question 3 Form 1" - , question: "Question 3 Form 1" - , type: InputType.TEXT - , validation: [ - { type: ValidationType.REGEX, arguments: ["\\d{5}-\\d{3}"] } - , { type: ValidationType.MANDATORY, arguments: [] } - ] - , id: 2 - }; - const formObj: FormOptions = { - id: 1 - , title: "Form Title 1" - , description: "Form Description 1" - , inputs: [ - OptHandler.input(inputObj1) - , OptHandler.input(inputObj2) - , OptHandler.input(inputObj3) - ] - }; - const formTmp: Form = new Form(OptHandler.form(formObj)); - - const inputAnswersOpt1: InputAnswerOptions = { - id: 1 - , idInput: null - , placement: 0 - , value: "Answer 1 to Question 1 Form 1" - }; - const inputAnswersOpt2: InputAnswerOptions = { - id: 2 - , idInput: null - , placement: 0 - , value: "Answer 1 to Question 2 Form" - }; - const inputAnswersOpt3: InputAnswerOptions = { - id: 3 - , idInput: null - , placement: 0 - , value: "Answer 1 to Question 3 Form" - }; - const inputAnswersOpt4: InputAnswerOptions = { - id: 4 - , idInput: null - , placement: 1 - , value: "Answer 2 to Question 3 Form" - }; - - const inputAnswerOptionsDict: InputAnswerOptionsDict = {1: [inputAnswersOpt1], 2: [inputAnswersOpt2], 3: [inputAnswersOpt3, inputAnswersOpt4]}; - const data: Date = new Date(2019, 5, 15); - const formAnswerOptions: any = { - id: 1 - , form: formTmp - , inputsAnswerOptions: inputAnswerOptionsDict - }; let formAnswerTmp: FormAnswer; - - try{ - formAnswerTmp = new FormAnswer(OptHandler.formAnswer(formAnswerOptions)); - }catch (e){ + try { + formAnswerTmp = new FormAnswer(OptHandler.formAnswer(optHandlerScenario.formHasNoDate)); + } catch (e) { expect(e).to.be.not.a("null"); expect(e.message).to.be.equal(ErrorHandler.notFound("Answer Date").message); expect(formAnswerTmp).to.be.a("undefined"); } - }); it("should get error when FormAnswer has a date associated but not type Date", () => { - - const inputObj1: InputOptions = { - placement: 0 - , description: "Description Question 1 Form 1" - , question: "Question 1 Form 1" - , type: InputType.TEXT - , validation: [] - , id: 1 - }; - - const inputObj2: InputOptions = { - placement: 1 - , description: "Description Question 2 Form 1" - , question: "Question 2 Form 1" - , type: InputType.TEXT - , validation: [ - { type: ValidationType.MAXCHAR, arguments: ["10"] } - , { type: ValidationType.MINCHAR, arguments: ["2"] } - ] - , id: 3 - }; - - const inputObj3: InputOptions = { - placement: 2 - , description: "Description Question 3 Form 1" - , question: "Question 3 Form 1" - , type: InputType.TEXT - , validation: [ - { type: ValidationType.REGEX, arguments: ["\\d{5}-\\d{3}"] } - , { type: ValidationType.MANDATORY, arguments: [] } - ] - , id: 2 - }; - const formObj: FormOptions = { - id: 1 - , title: "Form Title 1" - , description: "Form Description 1" - , inputs: [ - OptHandler.input(inputObj1) - , OptHandler.input(inputObj2) - , OptHandler.input(inputObj3) - ] - }; - const formTmp: Form = new Form(OptHandler.form(formObj)); - - const inputAnswersOpt1: InputAnswerOptions = { - id: 1 - , idInput: null - , placement: 0 - , value: "Answer 1 to Question 1 Form 1" - }; - const inputAnswersOpt2: InputAnswerOptions = { - id: 2 - , idInput: null - , placement: 0 - , value: "Answer 1 to Question 2 Form" - }; - const inputAnswersOpt3: InputAnswerOptions = { - id: 3 - , idInput: null - , placement: 0 - , value: "Answer 1 to Question 3 Form" - }; - const inputAnswersOpt4: InputAnswerOptions = { - id: 4 - , idInput: null - , placement: 1 - , value: "Answer 2 to Question 3 Form" - }; - - const inputAnswerOptionsDict: InputAnswerOptionsDict = {1: [inputAnswersOpt1], 2: [inputAnswersOpt2], 3: [inputAnswersOpt3, inputAnswersOpt4]}; - const data: Date = new Date(2019, 5, 15); - const formAnswerOptions: any = { - id: 1 - , form: formTmp - , timestamp: "data" - , inputsAnswerOptions: inputAnswerOptionsDict - }; let formAnswerTmp: FormAnswer; - - try{ - formAnswerTmp = new FormAnswer(OptHandler.formAnswer(formAnswerOptions)); - }catch (e){ + try { + formAnswerTmp = new FormAnswer(OptHandler.formAnswer(optHandlerScenario.formNoDateType)); + } catch (e) { expect(e).to.be.not.a("null"); expect(e.message).to.be.equal(ErrorHandler.notFound("Answer Date").message); expect(formAnswerTmp).to.be.a("undefined"); } - }); - it("should get error when FormAnswer has no inputAnswerOptionsDict associatedr", () => { - - const inputObj1: InputOptions = { - placement: 0 - , description: "Description Question 1 Form 1" - , question: "Question 1 Form 1" - , type: InputType.TEXT - , validation: [] - , id: 1 - }; - - const inputObj2: InputOptions = { - placement: 1 - , description: "Description Question 2 Form 1" - , question: "Question 2 Form 1" - , type: InputType.TEXT - , validation: [ - { type: ValidationType.MAXCHAR, arguments: ["10"] } - , { type: ValidationType.MINCHAR, arguments: ["2"] } - ] - , id: 3 - }; - - const inputObj3: InputOptions = { - placement: 2 - , description: "Description Question 3 Form 1" - , question: "Question 3 Form 1" - , type: InputType.TEXT - , validation: [ - { type: ValidationType.REGEX, arguments: ["\\d{5}-\\d{3}"] } - , { type: ValidationType.MANDATORY, arguments: [] } - ] - , id: 2 - }; - - const formObj: FormOptions = { - id: 1 - , title: "Form Title 1" - , description: "Form Description 1" - , inputs: [ - OptHandler.input(inputObj1) - , OptHandler.input(inputObj2) - , OptHandler.input(inputObj3) - ] - }; - - const formTmp: Form = new Form(OptHandler.form(formObj)); - - const inputAnswersOpt1: InputAnswerOptions = { - id: 1 - , idInput: null - , placement: 0 - , value: "Answer 1 to Question 1 Form 1" - }; - const inputAnswersOpt2: InputAnswerOptions = { - id: 2 - , idInput: null - , placement: 0 - , value: "Answer 1 to Question 2 Form" - }; - const inputAnswersOpt3: InputAnswerOptions = { - id: 3 - , idInput: null - , placement: 0 - , value: "Answer 1 to Question 3 Form" - }; - const inputAnswersOpt4: InputAnswerOptions = { - id: 4 - , idInput: null - , placement: 1 - , value: "Answer 2 to Question 3 Form" - }; - - const inputAnswerOptionsDict: InputAnswerOptionsDict = { - 1: [inputAnswersOpt1] - , 2: [inputAnswersOpt2] - , 3: [inputAnswersOpt3, inputAnswersOpt4] - }; - const data: Date = new Date(2019, 5, 15); - const formAnswerOptions: any = { - id: 1 - , form: formTmp - , timestamp: data - }; + it("should get error when FormAnswer has no inputAnswerOptionsDict associated", () => { let formAnswerTmp: FormAnswer; - - try{ - formAnswerTmp = new FormAnswer(OptHandler.formAnswer(formAnswerOptions)); - }catch (e){ + try { + formAnswerTmp = new FormAnswer(OptHandler.formAnswer(optHandlerScenario.formHasNoDict)); + } catch (e) { expect(e).to.be.not.a("null"); expect(e.message).to.be.equal(ErrorHandler.notFound("Input Answers").message); expect(formAnswerTmp).to.be.a("undefined"); } - }); it("should test InputAnswerOptions when missing idInput", () => { - - const inputObj1: InputOptions = { - placement: 0 - , description: "Description Question 1 Form 1" - , question: "Question 1 Form 1" - , type: InputType.TEXT - , validation: [] - , id: 1 - }; - - const inputObj2: InputOptions = { - placement: 1 - , description: "Description Question 2 Form 1" - , question: "Question 2 Form 1" - , type: InputType.TEXT - , validation: [ - { type: ValidationType.MAXCHAR, arguments: ["10"] } - , { type: ValidationType.MINCHAR, arguments: ["2"] } - ] - , id: 3 - }; - - const inputObj3: InputOptions = { - placement: 2 - , description: "Description Question 3 Form 1" - , question: "Question 3 Form 1" - , type: InputType.TEXT - , validation: [ - { type: ValidationType.REGEX, arguments: ["\\d{5}-\\d{3}"] } - , { type: ValidationType.MANDATORY, arguments: [] } - ] - , id: 2 - }; - const formObj: FormOptions = { - id: 1 - , title: "Form Title 1" - , description: "Form Description 1" - , inputs: [ - OptHandler.input(inputObj1) - , OptHandler.input(inputObj2) - , OptHandler.input(inputObj3) - ] - }; - const formTmp: Form = new Form(OptHandler.form(formObj)); - - const inputAnswersOpt1: any = { - id: 1 - , placement: 0 - , value: "Answer 1 to Question 1 Form 1" - }; - - const inputAnswerOptionsDict: InputAnswerOptionsDict = { - 1: [inputAnswersOpt1] - }; - const data: Date = new Date(2019, 5, 15); - const formAnswerOptions: FormAnswerOptions = { - id: 1 - , form: formTmp - , timestamp: data - , inputsAnswerOptions: inputAnswerOptionsDict - }; let formAnswerTmp: FormAnswer; - - try{ - formAnswerTmp = new FormAnswer(OptHandler.formAnswer(formAnswerOptions)); - }catch (e){ + try { + formAnswerTmp = new FormAnswer(OptHandler.formAnswer(optHandlerScenario.missingIdInputOpt)); + } catch (e) { expect(e).to.be.not.a("null"); expect(e.message).to.be.equal(ErrorHandler.notFound("idInput").message); expect(formAnswerTmp).to.be.a("undefined"); @@ -746,387 +210,49 @@ describe("Options Handler", () => { }); it("should test InputAnswerOptions when missing placement", () => { - - const inputObj1: InputOptions = { - placement: 0 - , description: "Description Question 1 Form 1" - , question: "Question 1 Form 1" - , type: InputType.TEXT - , validation: [] - , id: 1 - }; - - const inputObj2: InputOptions = { - placement: 1 - , description: "Description Question 2 Form 1" - , question: "Question 2 Form 1" - , type: InputType.TEXT - , validation: [ - { type: ValidationType.MAXCHAR, arguments: ["10"] } - , { type: ValidationType.MINCHAR, arguments: ["2"] } - ] - , id: 3 - }; - - const inputObj3: InputOptions = { - placement: 2 - , description: "Description Question 3 Form 1" - , question: "Question 3 Form 1" - , type: InputType.TEXT - , validation: [ - { type: ValidationType.REGEX, arguments: ["\\d{5}-\\d{3}"] } - , { type: ValidationType.MANDATORY, arguments: [] } - ] - , id: 2 - }; - const formObj: FormOptions = { - id: 1 - , title: "Form Title 1" - , description: "Form Description 1" - , inputs: [ - OptHandler.input(inputObj1) - , OptHandler.input(inputObj2) - , OptHandler.input(inputObj3) - ] - }; - const formTmp: Form = new Form(OptHandler.form(formObj)); - - const inputAnswersOpt1: any = { - id: 1 - , idInput: null - , value: "Answer 1 to Question 1 Form 1" - }; - - const inputAnswerOptionsDict: InputAnswerOptionsDict = { - 1: [inputAnswersOpt1] - }; - const data: Date = new Date(2019, 5, 15); - const formAnswerOptions: FormAnswerOptions = { - id: 1 - , form: formTmp - , timestamp: data - , inputsAnswerOptions: inputAnswerOptionsDict - }; let formAnswerTmp: FormAnswer; - - try{ - formAnswerTmp = new FormAnswer(OptHandler.formAnswer(formAnswerOptions)); - }catch (e){ + try { + formAnswerTmp = new FormAnswer(OptHandler.formAnswer(optHandlerScenario.missingPlacementInputOpt)); + } catch (e) { expect(e).to.be.not.a("null"); expect(e.message).to.be.equal(ErrorHandler.notFound("Placement").message); expect(formAnswerTmp).to.be.a("undefined"); } - }); it("should test InputAnswerOptions when missing Value", () => { - - const inputObj1: InputOptions = { - placement: 0 - , description: "Description Question 1 Form 1" - , question: "Question 1 Form 1" - , type: InputType.TEXT - , validation: [] - , id: 1 - }; - - const inputObj2: InputOptions = { - placement: 1 - , description: "Description Question 2 Form 1" - , question: "Question 2 Form 1" - , type: InputType.TEXT - , validation: [ - { type: ValidationType.MAXCHAR, arguments: ["10"] } - , { type: ValidationType.MINCHAR, arguments: ["2"] } - ] - , id: 3 - }; - - const inputObj3: InputOptions = { - placement: 2 - , description: "Description Question 3 Form 1" - , question: "Question 3 Form 1" - , type: InputType.TEXT - , validation: [ - { type: ValidationType.REGEX, arguments: ["\\d{5}-\\d{3}"] } - , { type: ValidationType.MANDATORY, arguments: [] } - ] - , id: 2 - }; - const formObj: FormOptions = { - id: 1 - , title: "Form Title 1" - , description: "Form Description 1" - , inputs: [ - OptHandler.input(inputObj1) - , OptHandler.input(inputObj2) - , OptHandler.input(inputObj3) - ] - }; - const formTmp: Form = new Form(OptHandler.form(formObj)); - - const inputAnswersOpt1: any = { - id: 1 - , idInput: null - , placement: 0 - }; - - const inputAnswerOptionsDict: InputAnswerOptionsDict = { - 1: [inputAnswersOpt1] - }; - const data: Date = new Date(2019, 5, 15); - const formAnswerOptions: FormAnswerOptions = { - id: 1 - , form: formTmp - , timestamp: data - , inputsAnswerOptions: inputAnswerOptionsDict - }; let formAnswerTmp: FormAnswer; - - try{ - formAnswerTmp = new FormAnswer(OptHandler.formAnswer(formAnswerOptions)); - }catch (e){ + try { + formAnswerTmp = new FormAnswer(OptHandler.formAnswer(optHandlerScenario.missingValueInputOpt)); + } catch (e) { expect(e).to.be.not.a("null"); expect(e.message).to.be.equal(ErrorHandler.notFound("Value").message); expect(formAnswerTmp).to.be.a("undefined"); } - }); it("should test FormAnswer no error", () => { - - const inputObj1: InputOptions = { - placement: 0 - , description: "Description Question 1 Form 1" - , question: "Question 1 Form 1" - , type: InputType.TEXT - , validation: [] - , id: 1 - }; - - const inputObj2: InputOptions = { - placement: 1 - , description: "Description Question 2 Form 1" - , question: "Question 2 Form 1" - , type: InputType.TEXT - , validation: [ - { type: ValidationType.MAXCHAR, arguments: ["10"] } - , { type: ValidationType.MINCHAR, arguments: ["2"] } - ] - , id: 3 - }; - - const inputObj3: InputOptions = { - placement: 2 - , description: "Description Question 3 Form 1" - , question: "Question 3 Form 1" - , type: InputType.TEXT - , validation: [ - { type: ValidationType.REGEX, arguments: ["\\d{5}-\\d{3}"] } - , { type: ValidationType.MANDATORY, arguments: [] } - ] - , id: 2 - }; - const formObj: FormOptions = { - id: 1 - , title: "Form Title 1" - , description: "Form Description 1" - , inputs: [ - OptHandler.input(inputObj1) - , OptHandler.input(inputObj2) - , OptHandler.input(inputObj3) - ] - }; - const formTmp: Form = new Form(OptHandler.form(formObj)); - - const inputAnswersOpt1: InputAnswerOptions = { - id: 1 - , idInput: null - , placement: 0 - , value: "Answer 1 to Question 1 Form 1" - }; - const inputAnswersOpt2: InputAnswerOptions = { - id: 2 - , idInput: null - , placement: 0 - , value: "Answer 1 to Question 2 Form" - }; - const inputAnswersOpt3: InputAnswerOptions = { - id: 3 - , idInput: null - , placement: 0 - , value: "Answer 1 to Question 3 Form" - }; - const inputAnswersOpt4: InputAnswerOptions = { - id: 4 - , idInput: null - , placement: 1 - , value: "Answer 2 to Question 3 Form" - }; - - const inputAnswerOptionsDict: InputAnswerOptionsDict = {1: [inputAnswersOpt1], 2: [inputAnswersOpt2], 3: [inputAnswersOpt3, inputAnswersOpt4]}; - const data: Date = new Date(2019, 5, 15); - const formAnswerOptions: any = { - id: 1 - , form: formTmp - , timestamp: data - , inputsAnswerOptions: inputAnswerOptionsDict - }; let formAnswerTmp: FormAnswer; - try{ - formAnswerTmp = new FormAnswer(OptHandler.formAnswer(formAnswerOptions)); + try { + formAnswerTmp = new FormAnswer(OptHandler.formAnswer(optHandlerScenario.validForm)); }finally{ expect(formAnswerTmp).to.not.be.a("undefined"); } }); it("should test FormUpdate no error", () => { - - const inputObj1: InputOptions = { - placement: 0 - , description: "Description Question 1 Form 1" - , question: "Question 1 Form 1" - , type: InputType.TEXT - , validation: [] - , id: 1 - }; - const updateObj1: InputUpdateOptions = { - id: 1 - , input: inputObj1 - , inputOperation: UpdateType.REMOVE - , value: null - }; - - const inputObj2: InputOptions = { - placement: 1 - , description: "Description Question 2 Form 1" - , question: "Question 2 Form 1" - , type: InputType.TEXT - , validation: [ - { type: ValidationType.MAXCHAR, arguments: ["10"] } - , { type: ValidationType.MINCHAR, arguments: ["2"] } - ] - , id: 3 - }; - const updateObj2: InputUpdateOptions = { - id: 2 - , input: inputObj2 - , inputOperation: UpdateType.REMOVE - , value: null - }; - - const inputObj3: InputOptions = { - placement: 2 - , description: "Description Question 3 Form 1" - , question: "Question 3 Form 1" - , type: InputType.TEXT - , validation: [ - { type: ValidationType.REGEX, arguments: ["\\d{5}-\\d{3}"] } - , { type: ValidationType.MANDATORY, arguments: [] } - ] - , id: 2 - }; - const updateObj3: InputUpdateOptions = { - id: 3 - , input: inputObj3 - , inputOperation: UpdateType.REMOVE - , value: null - }; - - const formObj: FormOptions = { - id: 1 - , title: "Form Title 1" - , description: "Form Description 1" - , inputs: [ - inputObj1 - , inputObj2 - , inputObj3 - ] - }; - const dateTMP: Date = new Date(); - const formUpdateObj: any = { - id: 1 - , form: formObj - , updateDate: dateTMP - , inputUpdates: [ - updateObj1 - , updateObj2 - , updateObj3 - ] - }; - let formUpdateTmp: FormUpdate; - try { - formUpdateTmp = new FormUpdate(OptHandler.formUpdate(formUpdateObj)); + formUpdateTmp = new FormUpdate(OptHandler.formUpdate(optHandlerScenario.validFormUpdate)); } finally { expect(formUpdateTmp).to.be.a("object"); } }); it("should get error FormUpdate inputUpdates is not an array", () => { - - const inputObj1: InputOptions = { - placement: 0 - , description: "Description Question 1 Form 1" - , question: "Question 1 Form 1" - , type: InputType.TEXT - , validation: [] - , id: 1 - }; - const updateObj: InputUpdateOptions = { - id: 1 - , input: inputObj1 - , inputOperation: UpdateType.REMOVE - , value: null - }; - - const inputObj2: InputOptions = { - placement: 1 - , description: "Description Question 2 Form 1" - , question: "Question 2 Form 1" - , type: InputType.TEXT - , validation: [ - { type: ValidationType.MAXCHAR, arguments: ["10"] } - , { type: ValidationType.MINCHAR, arguments: ["2"] } - ] - , id: 3 - }; - - const inputObj3: InputOptions = { - placement: 2 - , description: "Description Question 3 Form 1" - , question: "Question 3 Form 1" - , type: InputType.TEXT - , validation: [ - { type: ValidationType.REGEX, arguments: ["\\d{5}-\\d{3}"] } - , { type: ValidationType.MANDATORY, arguments: [] } - ] - , id: 2 - }; - - const formObj: FormOptions = { - id: 1 - , title: "Form Title 1" - , description: "Form Description 1" - , inputs: [ - inputObj1 - , inputObj2 - , inputObj3 - ] - }; - const dateTMP: Date = new Date(); - const formUpdateObj: any = { - id: 1 - , form: formObj - , updateDate: dateTMP - , inputUpdates: updateObj - }; - let formUpdateTmp: FormUpdate; - try { - formUpdateTmp = new FormUpdate(OptHandler.formUpdate(formUpdateObj)); + formUpdateTmp = new FormUpdate(OptHandler.formUpdate(optHandlerScenario.nonArrayInputUpdate)); } catch (e) { expect(e).to.be.not.a("null"); expect(e.message).to.be.equal(ErrorHandler.notFound("inputUpdates").message); @@ -1135,73 +261,9 @@ describe("Options Handler", () => { }); it("should get error FormUpdate missing option key 'form'", () => { - - const inputObj1: InputOptions = { - placement: 0 - , description: "Description Question 1 Form 1" - , question: "Question 1 Form 1" - , type: InputType.TEXT - , validation: [] - , id: 1 - }; - const updateObj1: InputUpdateOptions = { - id: 1 - , input: inputObj1 - , inputOperation: UpdateType.REMOVE - , value: null - }; - - const inputObj2: InputOptions = { - placement: 1 - , description: "Description Question 2 Form 1" - , question: "Question 2 Form 1" - , type: InputType.TEXT - , validation: [ - { type: ValidationType.MAXCHAR, arguments: ["10"] } - , { type: ValidationType.MINCHAR, arguments: ["2"] } - ] - , id: 3 - }; - const updateObj2: InputUpdateOptions = { - id: 2 - , input: inputObj2 - , inputOperation: UpdateType.REMOVE - , value: null - }; - - const inputObj3: InputOptions = { - placement: 2 - , description: "Description Question 3 Form 1" - , question: "Question 3 Form 1" - , type: InputType.TEXT - , validation: [ - { type: ValidationType.REGEX, arguments: ["\\d{5}-\\d{3}"] } - , { type: ValidationType.MANDATORY, arguments: [] } - ] - , id: 2 - }; - const updateObj3: InputUpdateOptions = { - id: 3 - , input: inputObj3 - , inputOperation: UpdateType.REMOVE - , value: null - }; - - const dateTMP: Date = new Date(); - const formUpdateObj: any = { - id: 1 - , updateDate: dateTMP - , inputUpdates: [ - updateObj1 - , updateObj2 - , updateObj3 - ] - }; - let formUpdateTmp: FormUpdate; - try { - formUpdateTmp = new FormUpdate(OptHandler.formUpdate(formUpdateObj)); + formUpdateTmp = new FormUpdate(OptHandler.formUpdate(optHandlerScenario.missingFormProperty)); } catch (e) { expect(e).to.be.not.a("null"); expect(e.message).to.be.equal(ErrorHandler.notFound("form").message); @@ -1210,62 +272,9 @@ describe("Options Handler", () => { }); it("should get error FormUpdate missing option key 'inputUpdates'", () => { - - const inputObj1: InputOptions = { - placement: 0 - , description: "Description Question 1 Form 1" - , question: "Question 1 Form 1" - , type: InputType.TEXT - , validation: [] - , id: 1 - }; - - const inputObj2: InputOptions = { - placement: 1 - , description: "Description Question 2 Form 1" - , question: "Question 2 Form 1" - , type: InputType.TEXT - , validation: [ - { type: ValidationType.MAXCHAR, arguments: ["10"] } - , { type: ValidationType.MINCHAR, arguments: ["2"] } - ] - , id: 3 - }; - - const inputObj3: InputOptions = { - placement: 2 - , description: "Description Question 3 Form 1" - , question: "Question 3 Form 1" - , type: InputType.TEXT - , validation: [ - { type: ValidationType.REGEX, arguments: ["\\d{5}-\\d{3}"] } - , { type: ValidationType.MANDATORY, arguments: [] } - ] - , id: 2 - }; - - const formObj: FormOptions = { - id: 1 - , title: "Form Title 1" - , description: "Form Description 1" - , inputs: [ - inputObj1 - , inputObj2 - , inputObj3 - ] - }; - - const dateTMP: Date = new Date(); - const formUpdateObj: any = { - id: 1 - , form: formObj - , updateDate: dateTMP - }; - let formUpdateTmp: FormUpdate; - try { - formUpdateTmp = new FormUpdate(OptHandler.formUpdate(formUpdateObj)); + formUpdateTmp = new FormUpdate(OptHandler.formUpdate(optHandlerScenario.missingInputUpdate)); } catch (e) { expect(e).to.be.not.a("null"); expect(e.message).to.be.equal(ErrorHandler.notFound("inputUpdates").message); @@ -1274,17 +283,9 @@ describe("Options Handler", () => { }); it("should get error InputUpdate missing option key 'input'", () => { - const updateObj: any = { - id: 1 - , input: undefined - , inputOperation: UpdateType.REMOVE - , value: null - }; - let updateTmp: InputUpdate; - try { - updateTmp = new InputUpdate(OptHandler.inputUpdate(updateObj)); + updateTmp = new InputUpdate(OptHandler.inputUpdate(optHandlerScenario.inputUpdateMissingInput)); } catch (e) { expect(e).to.be.not.a("null"); expect(e.message).to.be.equal(ErrorHandler.notFound("input").message); @@ -1293,26 +294,9 @@ describe("Options Handler", () => { }); it("should get error InputUpdate missing option key 'inputOperation'", () => { - - const inputObj: InputOptions = { - placement: 1 - , description: "Description Question 2 Form 1" - , question: "Question 2 Form 1" - , type: InputType.TEXT - , validation: [] - , id: 1 - }; - - const updateObj: any = { - id: 1 - , input: inputObj - , value: "Remove question 3 from form 1" - }; - let updateTmp: InputUpdate; - try { - updateTmp = new InputUpdate(OptHandler.inputUpdate(updateObj)); + updateTmp = new InputUpdate(OptHandler.inputUpdate(optHandlerScenario.missinginputOperation)); } catch (e) { expect(e).to.be.not.a("null"); expect(e.message).to.be.equal(ErrorHandler.notFound("inputOperation").message); @@ -1321,26 +305,9 @@ describe("Options Handler", () => { }); it("should get error InputUpdate missing option key 'value'", () => { - - const inputObj: InputOptions = { - placement: 1 - , description: "Description Question 2 Form 1" - , question: "Question 2 Form 1" - , type: InputType.TEXT - , validation: [] - , id: 1 - }; - - const updateObj: any = { - id: 1 - , input: inputObj - , inputOperation: UpdateType.REMOVE - }; - let updateTmp: InputUpdate; - try { - updateTmp = new InputUpdate(OptHandler.inputUpdate(updateObj)); + updateTmp = new InputUpdate(OptHandler.inputUpdate(optHandlerScenario.missingValueProperty)); } catch (e) { expect(e).to.be.not.a("null"); expect(e.message).to.be.equal(ErrorHandler.notFound("value").message); @@ -1349,24 +316,9 @@ describe("Options Handler", () => { }); it("should get error Input with malformed sugestion missing key 'placement'", () => { - - const inputObj: any = { - placement: 1 - , description: "Description Question 2 Form 1" - , question: "Question 2 Form 1" - , type: InputType.TEXT - , validation: [] - , sugestions: [ - { value: "Malformed Sugestion" } - , { value: "Sugestion", placement: 0 } - ] - , id: 1 - }; - let inputTmp: Input; - try { - inputTmp = new Input(OptHandler.input(inputObj)); + inputTmp = new Input(OptHandler.input(optHandlerScenario.malformedSugestionMissingPlacement)); } catch (e) { expect(e).to.be.not.a("null"); expect(e.message).to.be.equal(ErrorHandler.notFound("Sugestion placement").message); @@ -1375,28 +327,53 @@ describe("Options Handler", () => { }); it("should get error Input with malformed sugestion missing key 'value'", () => { + let inputTmp: Input; + try { + inputTmp = new Input(OptHandler.input(optHandlerScenario.malformedSugestionMissingValue)); + } catch (e) { + expect(e).to.be.not.a("null"); + expect(e.message).to.be.equal(ErrorHandler.notFound("Sugestion value").message); + expect(inputTmp).to.be.a("undefined"); + } + }); - const inputObj: any = { - placement: 1 - , description: "Description Question 2 Form 1" - , question: "Question 2 Form 1" - , type: InputType.TEXT - , validation: [] - , sugestions: [ - { placement: 0} - , { value: "Sugestion", placement: 1 } - ] - , id: 1 - }; + it("should test subForm no error", () => { + let inputTmp: Input; + try { + inputTmp = new Input(OptHandler.input(optHandlerScenario.validInputWithSubForm)); + } finally { + expect(inputTmp).to.be.a("object"); + expect(inputTmp.subForm).to.be.not.a("null"); + } + }); + it("should get error missing subForm", () => { let inputTmp: Input; + try { + inputTmp = new Input(OptHandler.input(optHandlerScenario.missingSubFormProperty)); + } catch (e) { + expect(e).to.be.not.a("null"); + expect(e.message).to.be.equal(ErrorHandler.notFound("Input subform").message); + } + }); + it("should get error missing subForm", () => { + let inputTmp: Input; try { - inputTmp = new Input(OptHandler.input(inputObj)); + inputTmp = new Input(OptHandler.input(optHandlerScenario.inputWithSubFormWithoutContentFormId)); } catch (e) { expect(e).to.be.not.a("null"); - expect(e.message).to.be.equal(ErrorHandler.notFound("Sugestion value").message); - expect(inputTmp).to.be.a("undefined"); + expect(e.message).to.be.equal(ErrorHandler.notFound("SubForm ContentForm").message); + } + }); + + it("should get error missing subForm", () => { + let inputTmp: Input; + try { + inputTmp = new Input(OptHandler.input(optHandlerScenario.inputWithSubFormWithoutInputId)); + } catch (e) { + expect(e).to.be.not.a("null"); + expect(e.message).to.be.equal(ErrorHandler.notFound("SubForm InputId").message); } }); }); diff --git a/src/utils/optHandler.ts b/src/utils/optHandler.ts index e2bc778c2930359b0de2b7fc2b55609a92f11e4c..b6639c4a5144b74b70cc8dbe3fe44ea60543ebdd 100644 --- a/src/utils/optHandler.ts +++ b/src/utils/optHandler.ts @@ -25,8 +25,11 @@ import { FormUpdate, FormUpdateOptions } from "../core/formUpdate"; import { InputOptions, Sugestion, Validation } from "../core/input"; import { InputAnswer, InputAnswerDict, InputAnswerOptions, InputAnswerOptionsDict } from "../core/inputAnswer"; import { InputUpdate, InputUpdateOptions } from "../core/inputUpdate"; +import { SubForm, SubFormOptions } from "../core/subForm"; +import { User, UserOptions } from "./../core/user"; import { InputType, UpdateType} from "./enumHandler"; import { ErrorHandler} from "./errorHandler"; + /** * OptHandler to handle an object and transform into a Classoptions to be used in Class's constructor */ @@ -64,21 +67,29 @@ export class OptHandler { * @returns - An InputOptions instance. */ public static input(obj: any): InputOptions{ - if (obj.placement === undefined){ + if (obj.placement === undefined) { throw ErrorHandler.notFound("Input placement"); } - if (obj.description === undefined){ + if (obj.description === undefined) { throw ErrorHandler.notFound("Input description"); } - if (obj.question === undefined){ + if (obj.question === undefined) { throw ErrorHandler.notFound("Input question"); } - if (obj.type === undefined){ + if (obj.type === undefined) { throw ErrorHandler.notFound("Input type"); } - if (obj.validation === undefined || !(obj.validation instanceof Array)){ + if (obj.validation === undefined || !(obj.validation instanceof Array)) { throw ErrorHandler.notFound("Input validation"); } + if ((obj.type === InputType.SUBFORM) && ((obj.subForm === undefined) || (obj.subForm === null))) { + throw ErrorHandler.notFound("Input subform"); + } + + let subForm: SubForm = null; + if ((obj.subForm !== null) && (obj.subForm !== undefined)) { + subForm = new SubForm (OptHandler.subForm(obj.subForm)); + } const option: InputOptions = { id: obj.id, @@ -90,7 +101,8 @@ export class OptHandler { validation: obj.validation.map((v: any) => { return {type: v.type, arguments: v.arguments}; }), - sugestions: [] + sugestions: [], + subForm }; if (obj.sugestions instanceof Array) { @@ -107,15 +119,14 @@ export class OptHandler { * @param obj - object that should be parsed. * @returns - An FormAnswerOptions instance. */ - public static formAnswer(obj: any): FormAnswerOptions{ - if (obj.form === undefined || !(obj.form instanceof Form)){ + public static formAnswer(obj: any): FormAnswerOptions { + if (obj.form === undefined || !(obj.form instanceof Form)) { throw ErrorHandler.notFound("Form"); } - if (obj.timestamp === undefined || !(obj.timestamp instanceof Date)){ + if (obj.timestamp === undefined || !(obj.timestamp instanceof Date)) { throw ErrorHandler.notFound("Answer Date"); } - - if (obj.inputsAnswerOptions === undefined ){ + if (obj.inputsAnswerOptions === undefined ) { throw ErrorHandler.notFound("Input Answers"); } @@ -141,22 +152,29 @@ export class OptHandler { * @param obj - object that should be parsed. * @returns - An InputAnswerOptions instance. */ - public static inputAnswer(obj: any): InputAnswerOptions{ - if (obj.idInput === undefined){ + public static inputAnswer(obj: any): InputAnswerOptions { + if (obj.idInput === undefined) { throw ErrorHandler.notFound("idInput"); } - if (obj.placement === undefined){ + if (obj.placement === undefined) { throw ErrorHandler.notFound("Placement"); } - if (obj.value === undefined){ + if (obj.value === undefined) { throw ErrorHandler.notFound("Value"); } + let subForm: FormAnswerOptions = null; + + if ((obj.subForm !== null) && (obj.subForm !== undefined)) { + subForm = this.formAnswer(obj.subForm); + } + const option: InputAnswerOptions = { id: obj.id , idInput: obj.idInput , placement: obj.placement , value: obj.value + , subForm }; return option; @@ -232,4 +250,56 @@ export class OptHandler { return option; } + + /** + * Return an UserOptions instance with a parsed and validated object. The main objective is to detect parsing errors previously + * @param obj - object to be parsed + * @returns - an UserOptions instance + */ + public static User(obj: any, hashedPw?: string): UserOptions { + if (obj.name === undefined) { + throw ErrorHandler.notFound("name"); + } + if (obj.email === undefined) { + throw ErrorHandler.notFound("email"); + } + + if (! hashedPw) { + const optionNoHash: UserOptions = { + name: obj.name + , email: obj.email + , id: obj.id + , forms: obj.forms + }; + + return optionNoHash; + } + + const option: UserOptions = { + name: obj.name + , email: obj.email + , id: obj.id + , hash: hashedPw + , forms: obj.forms + }; + + return option; + } + + public static subForm(obj: any): SubForm { + if (obj.inputId === undefined) { + throw ErrorHandler.notFound("SubForm InputId"); + } + if (obj.contentFormId === undefined) { + throw ErrorHandler.notFound("SubForm ContentForm"); + } + + const option: SubForm = { + id: obj.id + , inputId: obj.inputId + , contentFormId: obj.contentFormId + }; + + return option; + } } diff --git a/src/utils/scenarioHandler.ts b/src/utils/scenarioHandler.ts new file mode 100644 index 0000000000000000000000000000000000000000..89e7d8a30e7c3022f6aea61c5a2a1236db520ec3 --- /dev/null +++ b/src/utils/scenarioHandler.ts @@ -0,0 +1,33 @@ +/* + * form-creator-api. RESTful API to manage and answer forms. + * Copyright (C) 2019 Centro de Computacao Cientifica e Software Livre + * Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR + * + * This file is part of form-creator-api. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +export interface SortScenario { + /** + * Me deveolve alguma coisa desordenada, para testar + * sortByPlacement + */ + randomPlacement: any[]; + /** + * Me deveolve alguma coisa ordenada, para testar + * sortByPlacement + */ + orderedPlacement: any[]; +} diff --git a/src/utils/sorter.spec.ts b/src/utils/sorter.spec.ts index a8251e410087c2f63f682d6a6cc934f248d4bf66..3e4e89755dec2ba3f77e87693b59bf85ffaf75fa 100644 --- a/src/utils/sorter.spec.ts +++ b/src/utils/sorter.spec.ts @@ -20,42 +20,16 @@ */ import { expect } from "chai"; +import { sortScenario } from "../../test/scenario"; import { Sorter } from "./sorter"; describe("Sorter", () => { it("should sort objects with placement option", (done) => { - - const reqArray: any[] = [ - { placement: 5 } - , { placemente: 3 } - , { placemente: 1 } - , { placemente: 2 } - , { placemente: 1 } - , { placemente: 0 } - , { placemente: 4 } - , { placemente: 4 } - , { placemente: 3 } - , { placemente: 0 } - ]; - - const resArray: any[] = Sorter.sortByPlacement(reqArray); - - const expArray: any[] = [ - { placement: 0 } - , { placement: 0 } - , { placement: 1 } - , { placement: 1 } - , { placement: 2 } - , { placement: 3 } - , { placement: 3 } - , { placement: 4 } - , { placement: 4 } - , { placement: 5 } - ]; - + const resArray: any[] = Sorter.sortByPlacement(sortScenario.sortByPlacementRandom); + const expArray = sortScenario.orderedPlacement; for (let i = 0; i < resArray.length; i++) { - expect(resArray[i].placement).to.be.equal(reqArray[i].placement); + expect(resArray[i].placement).to.be.equal(expArray[i].placement); } done(); }); diff --git a/src/utils/testHandler.ts b/src/utils/testHandler.ts index f9cf76057d51f6a8c848fcd18f3c70b2ce8f6b74..3666abc9c36b14dd5d0444d7295c9cc9b65eadb4 100644 --- a/src/utils/testHandler.ts +++ b/src/utils/testHandler.ts @@ -26,6 +26,8 @@ import { FormUpdate } from "../core/formUpdate"; import { Input, Validation } from "../core/input"; import { InputAnswer } from "../core/inputAnswer"; import { InputUpdate } from "../core/inputUpdate"; +import { SubForm } from "../core/subForm"; +import { User } from "../core/user"; import { EnumHandler, InputType, UpdateType, ValidationType } from "./enumHandler"; import { Sorter } from "./sorter"; @@ -71,6 +73,12 @@ export class TestHandler { expect(input.validation[i].arguments[j]).to.be.equal(stub.validation[i].arguments[j]); } } + + if (input.type === InputType.SUBFORM) { + TestHandler.testSubForm(input.subForm, stub.subForm); + } else { + expect(input.subForm).to.be.equal(null); + } } /** @@ -88,7 +96,6 @@ export class TestHandler { }); } - expect(formAnswer.timestamp.toISOString()).to.be.equal(stub.timestamp.toISOString()); } @@ -103,6 +110,9 @@ export class TestHandler { expect(inputAnswer.idInput).to.be.equal(stub.idInput); expect(inputAnswer.placement).to.be.equal(stub.placement); expect(inputAnswer.value).to.be.equal(stub.value); + if (stub.subForm !== null) { + TestHandler.testFormAnswer(inputAnswer.subForm, stub.subForm); + } } /** @@ -134,4 +144,28 @@ export class TestHandler { expect(inputUpdate.inputOperation).to.be.equal(stub.inputOperation); expect(inputUpdate.value).to.be.equal(stub.value); } + + public static testUser(user: User, stub: User) { + /** If there's no ID input the DB itself should ID it */ + if (user.id === null) { + expect(stub.id).to.be.a("number"); + } else { + expect(user.id).to.be.equal(stub.id); + } + expect(user.name).to.be.equal(stub.name); + expect(user.email).to.be.equal(stub.email); + expect(user.hash).to.be.equal(stub.hash); + expect(user.enabled).to.be.equal(stub.enabled); + } + /** + * Verify if two sub forms are semantically equal; + * @param subForm - subForm that should be tested + * @param stub - A model subForm that first param itend to be. + * @returns - True if subForm are equal else false + */ + public static testSubForm(subForm: SubForm, stub: SubForm) { + expect(subForm.id).to.be.equal(stub.id); + expect(subForm.inputId).to.be.equal(stub.inputId); + expect(subForm.contentFormId).to.be.equal(stub.contentFormId); + } } diff --git a/src/utils/userQueryBuilder.ts b/src/utils/userQueryBuilder.ts new file mode 100644 index 0000000000000000000000000000000000000000..4c7c7370f729c61ba2e3bc75bd2966c3dfc6fb1d --- /dev/null +++ b/src/utils/userQueryBuilder.ts @@ -0,0 +1,432 @@ +/* + * form-creator-api. RESTful API to manage and answer forms. + * Copyright (C) 2019 Centro de Computacao Cientifica e Software Livre + * Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR + * + * This file is part of form-creator-api. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +import { eachSeries, map, waterfall } from "async"; +import { Pool, PoolConfig, QueryResult } from "pg"; +import { User, UserOptions } from "../core/user"; +import { ErrorHandler} from "./errorHandler"; +import { QueryBuilder, QueryOptions } from "./queryBuilder"; + +export class UserQueryBuilder extends QueryBuilder { + + constructor(pool: Pool) { + super(pool); + } + + /** + * Asynchronously read a User from database. + * @param id - User identifier to be founded. + * @param cb - Callback function which contains the data read. + * @param cb.err - Error information when the method fails. + * @param cb.User - User or null if User not exists. + */ + public read(id: number, cb: (err: Error, user?: User) => void) { + + waterfall([ + (callback: (err: Error, result?: QueryResult) => void) => { + this.begin((error: Error, results?: QueryResult) => { + callback(error); + }); + }, + + (callback: (err: Error, result?: User) => void) => { + this.readController(id, (error: Error, resultUser?: User) => { + callback(error, resultUser); + }); + }, + + (user: User, callback: (err: Error, result?: User) => void) => { + this.commit((error: Error, results?: QueryResult) => { + callback(error, user); + }); + } + + ], (err, user?: User) => { + if (err) { + this.rollback((error: Error, results?: QueryResult) => { + cb(err); + return; + }); + return; + } + cb(null, user); + }); + } + + /** + * Asynchronously read a User from database without transactions. + * @param id - User identifier to be founded. + * @param cb - Callback function which contains the data read. + * @param cb.err - Error information when the method fails. + * @param cb.User - User or null if User does not exists. + */ + private readController(id: number, cb: (err: Error, user?: User) => void) { + + this.executeReadUser(id, (error: Error, result: QueryResult) => { + + if (result.rowCount !== 1) { + cb(ErrorHandler.badIdAmount(result.rowCount)); + return; + } + + const userTmp: User = { + id: result.rows[0]["id"] + , name: result.rows[0]["name"] + , email: result.rows[0]["email"] + , hash: result.rows[0]["hash"] + , enabled: result.rows[0]["enabled"] + , forms: null + }; + + cb(error, userTmp); + }); + } + + /** + * Asynchronously read an User without transactions. + * @param id - user identifier to be founded. + * @param cb - Callback function which contains the data read. + * @param cb.err - Error information when the method fails. + * @param cb.user - User or null if User does not exists. + */ + private executeReadUser(id: number, cb: (err: Error, user?: QueryResult) => void) { + const queryString: string = "SELECT id, name, email, hash, enabled FROM form_user WHERE id=$1;"; + const query: QueryOptions = { + query: queryString + , parameters: [id] + }; + + this.executeQuery(query, (err: Error, result?: QueryResult) => { + cb(err, result); + }); + } + + /** + * Asynchronously write a user on database. + * @param user - User to be inserted. + * @param cb - Callback function which contains the inserted data. + * @param cb.err - Error information when the method fails. + * @param cb.UserResult - User or null if user any error occurs. + */ + public write(user: User, cb: (err: Error, user?: User) => void) { + + waterfall([ + (callback: (err: Error, result?: QueryResult) => void) => { + this.begin((error: Error, results?: QueryResult) => { + callback(error); + }); + }, + (callback: (err: Error, result?: number) => void) => { + this.writeController(user, (error: Error, resultId?: number) => { + callback(error, resultId); + }); + }, + (userId: number, callback: (err: Error, result?: number) => void) => { + this.commit((error: Error, results?: QueryResult) => { + callback(error, userId); + }); + }, + (userId: number, callback: (err: Error, result?: User) => void) => { + this.read(userId, (error: Error, resultUser?: User) => { + callback(error, resultUser); + }); + } + ], (err, userResult?: User) => { + if (err) { + this.rollback((error: Error, results?: QueryResult) => { + cb(err); + return; + }); + return; + } + cb(null, userResult); + }); + } + + /** + * Asynchronously write a user on database without transactions. + * @param user - User to be inserted. + * @param cb - Callback function which contains the inserted data. + * @param cb.err - Error information when the method fails. + * @param cb.formId - Form identifier or null if form any error occurs. + */ + private writeController(user: User, cb: (Err: Error, userId?: number) => void) { + waterfall([ + (callback: (err: Error) => void) => { + this.executeVerifyEmail (user.email, (err: Error, id?: number) => { + callback(err); + }); + }, + (callback: (err: Error, result?: number) => void) => { + this.executeWriteUser(user, (error: Error, resultId?: number) => { + callback(error, resultId); + }); + } + ], (err, id: number) => { + if (err) { + cb(err); + return; + } + cb(null, id); + }); + + } + + /** + * Asynchronously write a user on database. + * @param email - Email to be verified + * @param cb - Callback function which contains the error if there is any. + * @param cb.err - Error information when the method fails. + */ + public executeVerifyEmail(email: string, cb: (err: Error, id?: number) => void) { + const queryString: string = "SELECT id FROM form_user WHERE email = $1;"; + const query: QueryOptions = { + query: queryString + , parameters: [ email ] + }; + + this.executeQuery(query, (err: Error, result?: QueryResult) => { + if (err) { + cb(err); + return; + } + + if (result.rowCount !== 0) { + cb(new Error("Email exists on the database."), result.rows[0]["id"]); + return; + } + + cb(null); + }); + } + + /** + * Asynchronously insert an User on database without transactions. + * @param user - User to be inserted. + * @param cb - Callback function which contains informations about method's execution. + * @param cb.err - Error information when the method fails. + * @param cb.result - User identifier or null if any error occurs. + */ + private executeWriteUser(user: User, cb: (err: Error, result?: number) => void) { + const queryString: string = "INSERT INTO form_user (name, email, hash, enabled) \ + VALUES ($1, $2, $3, $4) \ + RETURNING id;"; + const query: QueryOptions = { + query: queryString + , parameters: [ + user.name + , user.email + , user.hash + , user.enabled + ] + }; + + this.executeQuery(query, (err: Error, result?: QueryResult) => { + if (err) { + cb(err); + return; + } + cb(null, result.rows[0]["id"]); + }); + } + + /** + * Asynchronously update a user on database. + * @param user- User to be updated. + * @param cb - Callback function which contains information about method's execution. + * @param cb.err - Error information when the method fails. + */ + public update(userUpdate: User, id: number, cb: (err: Error) => void) { + waterfall([ + (callback: (err: Error, result?: QueryResult) => void) => { + this.begin((error: Error, results?: QueryResult) => { + callback(error); + }); + }, + (callback: (err: Error, result?: User) => void) => { + this.executeUpdateUser(userUpdate, id, (error: Error) => { + callback(error); + }); + }, + (callback: (err: Error, result?: User) => void) => { + this.commit((error: Error, results?: QueryResult) => { + callback(error); + }); + } + ], (err) => { + if (err) { + this.rollback((error: Error, results?: QueryResult) => { + cb(err); + return; + }); + return; + } + cb(null); + }); + } + + /** + * Asynchronously update user's fields on database. + * @param value - An User to be inserted in database. + * @param id - The user id that should be updated. + * @param cb - Callback function. + * @param cb.err - Error information when method fails. + */ + private executeUpdateUser(value: User, id: number, cb: (err: Error) => void) { + const queryString: string = "UPDATE form_user SET name = $1, email = $2, enabled = $3 WHERE id = $4;"; + const query: QueryOptions = { + query: queryString + , parameters: [ + value.name + , value.email + , value.enabled + , id + ] + }; + this.executeQuery(query, (err: Error, result?: QueryResult) => { + cb(err); + }); + } + + /** + * Asynchronously delete an user from database. + * @param user- User to be deleted. + * @param cb - Callback function which contains information about method's execution. + * @param cb.err - Error information when the method fails. + */ + public delete(id: number, cb: (err: Error) => void) { + + waterfall([ + (callback: (err: Error, result?: QueryResult) => void) => { + this.begin((error: Error, results?: QueryResult) => { + callback(error); + }); + }, + + (callback: (err: Error, result?: User) => void) => { + this.deleteUserController(id, (error: Error, resultUser?: User) => { + callback(error, resultUser); + }); + }, + + (user: User, callback: (err: Error, result?: User) => void) => { + this.commit((error: Error, results?: QueryResult) => { + callback(error, user); + }); + } + + ], (err) => { + if (err) { + this.rollback((error: Error, results?: QueryResult) => { + cb(err); + return; + }); + return; + } + cb(null); + }); + } + + private deleteUserController(id: number, cb: (error: Error, resultUser?: User) => void) { + + this.executeDeleteUser(id, (error: Error, result?: QueryResult) => { + + if (result.rowCount !== 1) { + cb(new Error("Invalid ID, user doesn't exist.")); + return; + } + + cb(null); + }); + + } + + private executeDeleteUser(id: number, cb: (err: Error, result?: QueryResult) => void) { + const queryString: string = "DELETE FROM form_user WHERE id=$1;"; + const query: QueryOptions = { + query: queryString + , parameters: [ id ] + }; + + this.executeQuery(query, (err: Error, result?: QueryResult) => { + cb(err, result); + }); + } + + /** + * Asynchronously assigns an user to a form. + * @param userId - User ID to be assigned to the form. + * @param formId - Form ID to be assigned to the user. + * @param cb - Callback function which contains information about method's execution. + * @param cb.err - Error information when the method fails. + */ + public assign(userId: number, formId: number, cb: (err: Error) => void) { + waterfall([ + (callback: (err: Error, result?: QueryResult) => void) => { + this.begin((error: Error, results?: QueryResult) => { + callback(error); + }); + }, + (callback: (err: Error, result?: User) => void) => { + this.executeUpdateOwnerList(userId, formId, (error: Error) => { + callback(error); + }); + }, + (callback: (err: Error, result?: User) => void) => { + this.commit((error: Error, results?: QueryResult) => { + callback(error); + }); + } + ], (err) => { + if (err) { + this.rollback((error: Error, results?: QueryResult) => { + cb(err); + return; + }); + } + cb(null); + }); + } + + /** + * Asynchronously assigns an user to a form in the database. + * @param userId - User ID to be assigned to the form. + * @param formId - Form ID to be assigned to the user. + * @param cb - Callback function. + * @param cb.err - Error information when the method fails. + */ + private executeUpdateOwnerList(userId: number, formId: number, cb: (err: Error) => void) { + const queryString: string = "INSERT INTO form_owner (id_user, id_form) \ + VALUES ( $1, $2 );"; + const query: QueryOptions = { + query: queryString + , parameters: [ + userId + , formId + ] + }; + + this.executeQuery(query, (err: Error, result?: QueryResult) => { + cb(err); + }); + } + +} diff --git a/src/utils/validationHandler.spec.ts b/src/utils/validationHandler.spec.ts index 996ade015ec5f63cd983f9d1b52d98e4ef92477c..c6e86e4d4b7f2d807f641144a5cb3073b8a122f7 100644 --- a/src/utils/validationHandler.spec.ts +++ b/src/utils/validationHandler.spec.ts @@ -20,6 +20,7 @@ */ import { expect } from "chai"; +import { validationHScenario } from "../../test/scenario"; import { Form } from "../core/form"; import { FormAnswer, FormAnswerOptions } from "../core/formAnswer"; import { InputAnswerOptions, InputAnswerOptionsDict } from "../core/inputAnswer"; @@ -33,22 +34,10 @@ describe("Validation Handler", () => { const dbhandler = new DbHandler(configs.poolconfig); it("should test when Input has a minimum char number", (done) => { - const inputAnswersOpt1: InputAnswerOptions = { - idInput: 4 - , placement: 0 - , value: "MIN 8" - }; - const inputAnswersOpt2: InputAnswerOptions = { - idInput: 5 - , placement: 0 - , value: "Answer to Question 2 Form 2" - }; - const inputAnswerOptionsDict: InputAnswerOptionsDict = { - 4: [inputAnswersOpt1] - , 5: [inputAnswersOpt2] - }; - const date: Date = new Date(2019, 6, 4); + const inputAnswerOptionsDict = validationHScenario.dictMinCharNumber; + + const date: Date = validationHScenario.date; dbhandler.form.read(2, (error: Error, form: Form) => { expect(error).to.be.a("null"); @@ -69,22 +58,10 @@ describe("Validation Handler", () => { }); it("should test when Input is mandatory", (done) => { - const inputAnswersOpt1: InputAnswerOptions = { - idInput: 4 - , placement: 0 - , value: "Answer to Question 1 Form 2" - }; - const inputAnswersOpt2: InputAnswerOptions = { - idInput: 5 - , placement: 0 - , value: "" - }; - const inputAnswerOptionsDict: InputAnswerOptionsDict = { - 4: [inputAnswersOpt1] - , 5: [inputAnswersOpt2] - }; - const data: Date = new Date(2019, 6, 4); + const inputAnswerOptionsDict = validationHScenario.dictMandatoryInput; + + const data: Date = validationHScenario.date; dbhandler.form.read(2, (error: Error, form: Form) => { const formAnswerOptions: FormAnswerOptions = { form @@ -96,7 +73,7 @@ describe("Validation Handler", () => { ValidationHandler.validateFormAnswer(formAnswer); } catch (e) { expect(e.validationDict["4"]).to.be.a("undefined"); - expect(e.validationDict["5"]).to.be.equal("Input answer is mandatory"); + expect(e.validationDict["5"]).to.be.equal("Input answer is mandatory;Input answer is mandatory"); } done(); }); @@ -104,27 +81,8 @@ describe("Validation Handler", () => { it("should test when Input has a maximum char number", (done) => { - const inputAnswersOpt1: InputAnswerOptions = { - idInput: 1 - , placement: 0 - , value: "Answer to Question 1 Form 1" - }; - const inputAnswersOpt2: InputAnswerOptions = { - idInput: 2 - , placement: 0 - , value: "12345-000" - }; - const inputAnswersOpt3: InputAnswerOptions = { - idInput: 3 - , placement: 0 - , value: "MORE THEN 10 CHAR" - }; - const inputAnswerOptionsDict: InputAnswerOptionsDict = { - 1: [inputAnswersOpt1] - , 2: [inputAnswersOpt2] - , 3: [inputAnswersOpt3] - }; - const data: Date = new Date(2019, 6, 4); + const inputAnswerOptionsDict = validationHScenario.dictMaxCharNumber; + const data: Date = validationHScenario.date; dbhandler.form.read(1, (error: Error, form: Form) => { const formAnswerOptions: FormAnswerOptions = { form @@ -146,27 +104,8 @@ describe("Validation Handler", () => { it("should test when Input has a RegEx", (done) => { - const inputAnswersOpt1: InputAnswerOptions = { - idInput: 1 - , placement: 0 - , value: "Answer to Question 1 Form 1" - }; - const inputAnswersOpt2: InputAnswerOptions = { - idInput: 2 - , placement: 0 - , value: "12aa345-000" - }; - const inputAnswersOpt3: InputAnswerOptions = { - idInput: 3 - , placement: 0 - , value: "MAXCHAR 10" - }; - const inputAnswerOptionsDict: InputAnswerOptionsDict = { - 1: [inputAnswersOpt1] - , 2: [inputAnswersOpt2] - , 3: [inputAnswersOpt3] - }; - const data: Date = new Date(2019, 6, 4); + const inputAnswerOptionsDict = validationHScenario.dictHasARegEx; + const data: Date = validationHScenario.date; dbhandler.form.read(1, (error: Error, form: Form) => { const formAnswerOptions: FormAnswerOptions = { form @@ -187,72 +126,7 @@ describe("Validation Handler", () => { it("should test when input is number", (done) => { - const inputAnswerOpt1: InputAnswerOptions = { - idInput: 23 - , placement: 0 - , value: "Not a number" - }; - - const inputAnswerOpt2: InputAnswerOptions = { - idInput: 23 - , placement: 0 - , value: "23" - }; - - const inputAnswerOpt3: InputAnswerOptions = { - idInput: 23 - , placement: 0 - , value: "24" - }; - - const inputAnswerOpt4: InputAnswerOptions = { - idInput: 24 - , placement: 0 - , value: "25" - }; - - const inputAnswerOpt5: InputAnswerOptions = { - idInput: 25 - , placement: 0 - , value: "Not a Float" - }; - - const inputAnswerOpt6: InputAnswerOptions = { - idInput: 26 - , placement: 0 - , value: "1.83" - }; - const inputAnswerOpt7: InputAnswerOptions = { - idInput: 27 - , placement: 0 - , value: "Not a date" - }; - - const inputAnswerOpt8: InputAnswerOptions = { - idInput: 28 - , placement: 0 - , value: "02/02/2002" - }; - - const inputAnswerOpt9: InputAnswerOptions = { - idInput: 29 - , placement: 0 - , value: "Invalid argument causes invalid answer" - }; - - const inputAnswerOptionsDict: InputAnswerOptionsDict = { - 23: [ - inputAnswerOpt1 - , inputAnswerOpt2 - , inputAnswerOpt3 - ] - , 24: [inputAnswerOpt4] - , 25: [inputAnswerOpt5] - , 26: [inputAnswerOpt6] - , 27: [inputAnswerOpt7] - , 28: [inputAnswerOpt8] - , 29: [inputAnswerOpt9] - }; + const inputAnswerOptionsDict = validationHScenario.dictCNFD; dbhandler.form.read(7, (error: Error, form: Form) => { const formAnswerOptions: FormAnswerOptions = { @@ -278,45 +152,7 @@ describe("Validation Handler", () => { it("should test when input has sugestion", (done) => { - const inputAnswerOpt1: InputAnswerOptions = { - idInput: 18 - , placement: 5 - , value: "Invalid Placement" - }; - - const inputAnswerOpt2: InputAnswerOptions = { - idInput: 19 - , placement: 1 - , value: "true" - }; - - const inputAnswerOpt3: InputAnswerOptions = { - idInput: 20 - , placement: 1 - , value: "true" - }; - - const inputAnswerOpt4: InputAnswerOptions = { - idInput: 21 - , placement: 0 - , value: "true" - }; - - const inputAnswerOpt5: InputAnswerOptions = { - idInput: 22 - , placement: 0 - , value: "Answer question 5 form 6" - }; - - const inputAnswerOptionsDict: InputAnswerOptionsDict = { - 18: [ - inputAnswerOpt1 - ] - , 19: [inputAnswerOpt2] - , 20: [inputAnswerOpt3] - , 21: [inputAnswerOpt4] - , 22: [inputAnswerOpt5] - }; + const inputAnswerOptionsDict: InputAnswerOptionsDict = validationHScenario.dictHasSugestion; dbhandler.form.read(6, (error: Error, form: Form) => { const formAnswerOptions: FormAnswerOptions = { diff --git a/test/fixture.ts b/test/fixture.ts new file mode 100644 index 0000000000000000000000000000000000000000..388d406ff5bdcbeecc41a7b73b528b074eb31c6a --- /dev/null +++ b/test/fixture.ts @@ -0,0 +1,52 @@ +/* + * form-creator-api. RESTful API to manage and answer forms. + * Copyright (C) 2019 Centro de Computacao Cientifica e Software Livre + * Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR + * + * This file is part of form-creator-api. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +import * as child from "child_process"; +import * as fs from "fs"; +import * as path from "path"; + +export class Fixture { + private scriptPath: string; + private workspacePath: string; + + constructor() { + this.scriptPath = path.normalize(__dirname + "/../form-creator-database/psql-manager/manager.sh"); + this.workspacePath = path.normalize(__dirname + "/../form-creator-database/workspace"); + } + + public drop(cb: (err: Error) => void): void { + const exec: child.ChildProcess = child.execFile(this.scriptPath, ["psql", "drop"], (error: Error) => { + cb(error); + }); + } + + public create(cb: (err: Error) => void): void { + const exec: child.ChildProcess = child.execFile(this.scriptPath, ["psql", "create", this.workspacePath], (error: Error) => { + cb(error); + }); + } + + public fixture(cb: (err: Error) => void): void { + const exec: child.ChildProcess = child.execFile(this.scriptPath, ["psql", "fixture", this.workspacePath], (error: Error) => { + cb(error); + }); + } +} diff --git a/test/scenario.ts b/test/scenario.ts new file mode 100644 index 0000000000000000000000000000000000000000..e8bcd67708daa5cd16f04c3dc3b3cad88682f57a --- /dev/null +++ b/test/scenario.ts @@ -0,0 +1,4008 @@ +/* + * form-creator-api. RESTful API to manage and answer forms. + * Copyright (C) 2019 Centro de Computacao Cientifica e Software Livre + * Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR + * + * This file is part of form-creator-api. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +import { Form, FormOptions } from "../src/core/form"; +import { FormAnswer, FormAnswerOptions } from "../src/core/formAnswer"; +import { FormUpdate, FormUpdateOptions } from "../src/core/formUpdate"; +import { Input, InputOptions, Validation } from "../src/core/input"; +import { InputAnswerOptions, InputAnswerOptionsDict } from "../src/core/inputAnswer"; +import { InputUpdate, InputUpdateOptions } from "../src/core/inputUpdate"; +import { SubForm, SubFormOptions } from "../src/core/subForm"; +import { User } from "../src/core/user"; +import { EnumHandler, InputType, UpdateType, ValidationType } from "../src/utils/enumHandler"; +import { OptHandler } from "../src/utils/optHandler"; +import { QueryBuilder, QueryOptions } from "../src/utils/queryBuilder"; +/** + * testing sort.spec.ts + */ +/** Array with random placement values */ +const randomPlacement: any[] = [ + { placement: 5 } + , { placement: 3 } + , { placement: 1 } + , { placement: 2 } + , { placement: 1 } + , { placement: 0 } + , { placement: 4 } + , { placement: 4 } + , { placement: 3 } + , { placement: 0 } +]; +/** Array with sorted placement values */ +const orderedPlacement: any[] = [ + { placement: 0 } + , { placement: 0 } + , { placement: 1 } + , { placement: 1 } + , { placement: 2 } + , { placement: 3 } + , { placement: 3 } + , { placement: 4 } + , { placement: 4 } + , { placement: 5 } +]; + +/** ====================================================== */ + +/** The same 'Date' value was used in some objects */ +const date: Date = new Date (2019, 6, 4); + +/** input with value under the minimum char number */ +const inputMinCharNumber: InputAnswerOptions = { + idInput: 4 + , placement: 0 + , value: "MIN 8" +}; +/** comparing with min input char */ +const inputQuest2Form2: InputAnswerOptions = { + idInput: 5 + , placement: 0 + , value: "Answer to Question 2 Form 2" +}; +/** dict for testing min char number */ +const AnswerDictMinCharNumber: InputAnswerOptionsDict = { + 4: [inputMinCharNumber] + , 5: [inputQuest2Form2] +}; +/** comparing with mandatory input */ +const inputQuest1Fomr2: InputAnswerOptions = { + idInput: 4 + , placement: 0 + , value: "Answer to Question 1 Form 2" +}; +/** test mandatory input */ +const lackingInputValueAnswer: InputAnswerOptions = { + idInput: 5 + , placement: 0 + , value: "" +}; +/** dict for testing mandatory input */ +const AnswerDictMandatoryInput: InputAnswerOptionsDict = { + 4: [inputQuest1Fomr2] + , 5: [lackingInputValueAnswer] +}; +/** being compared with max char, 'has regex' */ +const AnswerQuest1Form1: InputAnswerOptions = { + idInput: 1 + , placement: 0 + , value: "Answer to Question 1 Form 1" +}; +/** being compared with max char */ +const AnswerNumValue: InputAnswerOptions = { + idInput: 2 + , placement: 0 + , value: "12345-000" +}; +/** test maximun char */ +const AnswerMoreThan10Char: InputAnswerOptions = { + idInput: 3 + , placement: 0 + , value: "MORE THEN 10 CHAR" +}; +/** dict for testing maximun char number */ +const AnswerDictMaxChar: InputAnswerOptionsDict = { + 1: [AnswerQuest1Form1] + , 2: [AnswerNumValue] + , 3: [AnswerMoreThan10Char] +}; +/** test having a RegEx */ +const AnswerRegEx: InputAnswerOptions = { + idInput: 2 + , placement: 0 + , value: "12aa345-000" +}; +/** being compared with having a RegEx */ +const AnswerMaxChar10: InputAnswerOptions = { + idInput: 3 + , placement: 0 + , value: "MAXCHAR 10" +}; +/** dict for testing when the input has an Regular expression that does not match */ +const DictWithRegExInput: InputAnswerOptionsDict = { + 1: [AnswerNumValue] + , 2: [AnswerRegEx] + , 3: [AnswerMaxChar10] +}; +/** test condition for input being int and to not have more than 2 answers */ +const AnswerNaN: InputAnswerOptions = { + idInput: 23 + , placement: 0 + , value: "Not a number" +}; +/** test having more than 2 answer */ +const Answer23: InputAnswerOptions = { + idInput: 23 + , placement: 0 + , value: "23" +}; +/** test having more than 2 answer */ +const Answer23conflict: InputAnswerOptions = { + idInput: 23 + , placement: 0 + , value: "24" +}; +/** + * expected answer + */ +const AnswerOK: InputAnswerOptions = { + idInput: 24 + , placement: 0 + , value: "25" +}; +/** + * test an answer not being a float when + */ +const AnsweNotaFloat: InputAnswerOptions = { + idInput: 25 + , placement: 0 + , value: "Not a Float" +}; +/** + * espected float answer + */ +const AnswerFloat: InputAnswerOptions = { + idInput: 26 + , placement: 0 + , value: "1.83" +}; +/** + * answer not having an date when required + */ +const AnswerNotaDate: InputAnswerOptions = { + idInput: 27 + , placement: 0 + , value: "Not a date" +}; +/** + * expected date answer + */ +const AnswerDate: InputAnswerOptions = { + idInput: 28 + , placement: 0 + , value: "02/02/2002" +}; +/** + * invalid answer + */ +const AnswerInvalid: InputAnswerOptions = { + idInput: 29 + , placement: 0 + , value: "Invalid argument causes invalid answer" +}; +/** Dictionary to test wrong inputAnswerOptions */ +const AnswerOptionsDict: InputAnswerOptionsDict = { + 23: [ + AnswerNaN + , Answer23 + , Answer23conflict + ] + , 24: [AnswerOK] + , 25: [AnsweNotaFloat] + , 26: [AnswerFloat] + , 27: [AnswerNotaDate] + , 28: [AnswerDate] + , 29: [AnswerInvalid] +}; +/** AnswerOpt with invalid placement */ +const notAnswer: InputAnswerOptions = { + idInput: 18 + , placement: 5 + , value: "Invalid Placement" +}; +/** Answer with 'true' value */ +const AnswerTrue: InputAnswerOptions = { + idInput: 19 + , placement: 1 + , value: "true" +}; +/** Answer with 'true' value */ +const AnswerTrue2nd: InputAnswerOptions = { + idInput: 20 + , placement: 1 + , value: "true" +}; +/** Answer with 'true' value */ +const AnswerTrue3rd: InputAnswerOptions = { + idInput: 21 + , placement: 0 + , value: "true" +}; +/** Answer with a wrong ID */ +const AnswerWrongId: InputAnswerOptions = { + idInput: 22 + , placement: 0 + , value: "Answer question 5 form 6" +}; +/** Dictionary to test some properties */ +const AnswerDictHasSugestionInput: InputAnswerOptionsDict = { + 18: [notAnswer] + , 19: [AnswerTrue] + , 20: [AnswerTrue2nd] + , 21: [AnswerTrue3rd] + , 22: [AnswerWrongId] +}; +/** ====================================================== */ + +/** + * testing diffHandler.spec.ts -- compare new inputs with the old ones + * Input(ordered by description) -> forms -> expectedInput -> expectedForm + */ + +/** + * input to be compared; + * without validation value + */ +const Input1: Input = { + placement: 0 + , description: "Description Question 1 Form 1" + , question: "Question 1 Form 1" + , enabled: true + , type: InputType.TEXT + , validation: [] + , id: 1 + , sugestions: [] + , subForm: null +}; +/** input to be compared */ +const Input1Otherplacement: Input = { + placement: 1 + , description: "Description Question 1 Form 1" + , question: "Question 1 Form 1" + , enabled: true + , type: InputType.TEXT + , validation: [] + , id: 1 + , sugestions: [] + , subForm: null +}; +/** input to be compared */ +const Input1UndefinedID: Input = { + placement: 0 + , description: "Description Question 1 Form 1" + , question: "Question 1 Form 1" + , enabled: true + , type: InputType.TEXT + , validation: [] + , id: undefined + , sugestions: [] + , subForm: null +}; +/** input to be compared */ +const Input1Empty: Input = { + placement: 1 + , description: "Description Question 1 Form 1" + , question: "Question 1 Form 1" + , enabled: true + , type: InputType.TEXT + , validation: [] + , id: null + , sugestions: [] + , subForm: null +}; + +/** input to be compared */ +const Input2: Input = { + placement: 1 + , description: "Description Question 2 Form 1" + , question: "Question 2 Form 1" + , enabled: true + , type: InputType.TEXT + , validation: [ + { type: ValidationType.MAXCHAR, arguments: ["10"] } + , { type: ValidationType.MINCHAR, arguments: ["2"] } + ] + , id: 3 + , sugestions: [] + , subForm: null +}; +/** input to be compared */ +const Input2Placement0: Input = { + placement: 0 + , description: "Description Question 2 Form 1" + , question: "Question 2 Form 1" + , enabled: true + , type: InputType.TEXT + , validation: [ + { type: ValidationType.MAXCHAR, arguments: ["10"] } + , { type: ValidationType.MINCHAR, arguments: ["2"] } + ] + , id: 3 + , sugestions: [] + , subForm: null +}; +/** input to be compared */ +const Input2UndefinedID: Input = { + placement: 1 + , description: "Description Question 2 Form 1" + , question: "Question 2 Form 1" + , enabled: true + , type: InputType.TEXT + , validation: [ + { type: ValidationType.MAXCHAR, arguments: ["10"] } + , { type: ValidationType.MINCHAR, arguments: ["2"] } + ] + , id: undefined + , sugestions: [] + , subForm: null +}; +/** input to be compared */ +const Input2Placement0id2: Input = { + placement: 0 + , description: "Description Question 2 Form 1" + , question: "Question 2 Form 1" + , enabled: true + , type: InputType.TEXT + , validation: [ + { type: ValidationType.MAXCHAR, arguments: ["10"] } + , { type: ValidationType.MINCHAR, arguments: ["2"] } + ] + , id: 2 + , sugestions: [] + , subForm: null +}; +/** input to be compared */ +const Input2id2: Input = { + placement: 1 + , description: "Description Question 2 Form 1" + , question: "Question 2 Form 1" + , enabled: true + , type: InputType.TEXT + , validation: [ + { type: ValidationType.MAXCHAR, arguments: ["10"] } + , { type: ValidationType.MINCHAR, arguments: ["2"] } + ] + , id: 2 + , sugestions: [] + , subForm: null +}; +/** input to be compared */ +const Input2Placement2id2: Input = { + placement: 2 + , description: "Description Question 2 Form 1" + , question: "Question 2 Form 1" + , enabled: true + , type: InputType.TEXT + , validation: [ + { type: ValidationType.MAXCHAR, arguments: ["10"] } + , { type: ValidationType.MINCHAR, arguments: ["2"] } + ] + , id: 2 + , sugestions: [] + , subForm: null +}; +/** input to be compared */ +const Input2Placement0idNULL: Input = { + placement: 0 + , description: "Description Question 2 Form 1" + , question: "Question 2 Form 1" + , enabled: true + , type: InputType.TEXT + , validation: [ + { type: ValidationType.MAXCHAR, arguments: ["10"] } + , { type: ValidationType.MINCHAR, arguments: ["2"] } + ] + , id: null + , sugestions: [] + , subForm: null +}; +/** input to be compared */ +const Input3: Input = { + placement: 2 + , description: "Description Question 3 Form 1" + , question: "Question 3 Form 1" + , enabled: true + , type: InputType.TEXT + , validation: [ + { type: ValidationType.REGEX, arguments: ["\\d{5}-\\d{3}"] } + , { type: ValidationType.MANDATORY, arguments: [] } + ] + , id: 2 + , sugestions: [] + , subForm: null +}; + +/** input to be compared */ +const Input3UndefinedID: Input = { + placement: 2 + , description: "Description Question 3 Form 1" + , question: "Question 3 Form 1" + , enabled: true + , type: InputType.TEXT + , validation: [ + { type: ValidationType.REGEX, arguments: ["\\d{5}-\\d{3}"] } + , { type: ValidationType.MANDATORY, arguments: [] } + ] + , id: undefined + , sugestions: [] + , subForm: null +}; +/** input to be compared */ +const Input3OtherID: Input = { + placement: 2 + , description: "Description Question 3 Form 1" + , question: "Question 3 Form 1" + , enabled: true + , type: InputType.TEXT + , validation: [ + { type: ValidationType.REGEX, arguments: ["\\d{5}-\\d{3}"] } + , { type: ValidationType.MANDATORY, arguments: [] } + ] + , id: 3 + , sugestions: [] + , subForm: null +}; +/** input to be compared */ +const Input3Placement1id3: Input = { + placement: 1 + , description: "Description Question 3 Form 1" + , question: "Question 3 Form 1" + , enabled: true + , type: InputType.TEXT + , validation: [ + { type: ValidationType.REGEX, arguments: ["\\d{5}-\\d{3}"] } + , { type: ValidationType.MANDATORY, arguments: [] } + ] + , id: 3 + , sugestions: [] + , subForm: null +}; +/** input to be compared */ +const Input3idNULL: Input = { + placement: 2 + , description: "Description Question 3 Form 1" + , question: "Question 3 Form 1" + , enabled: true + , type: InputType.TEXT + , validation: [ + { type: ValidationType.REGEX, arguments: ["\\d{5}-\\d{3}"] } + , { type: ValidationType.MANDATORY, arguments: [] } + ] + , id: null + , sugestions: [] + , subForm: null +}; +/** input to be compared */ +const Input4: Input = { + placement: 3 + , description: "Description Question 4 Form 1" + , question: "Question 4 Form 1" + , enabled: true + , type: InputType.TEXT + , validation: [] + , id: undefined + , sugestions: [] + , subForm: null +}; +/** input to be compared */ +const Input4Placement2id4: Input = { + placement: 2 + , description: "Description Question 4 Form 1" + , question: "Question 4 Form 1" + , enabled: true + , type: InputType.TEXT + , validation: [ + { type: ValidationType.MAXCHAR, arguments: ["10"] } + , { type: ValidationType.MINCHAR, arguments: ["2"] } + ] + , id: 4 + , sugestions: [] + , subForm: null +}; +/** input to be compared */ +const mixedInput1: Input = { + placement: 1 + , description: "Description Question 2 Form 1" + , question: "Question 3 Form 1" + , enabled: true + , type: InputType.TEXT + , validation: [ + { type: ValidationType.MAXCHAR, arguments: ["10"] } + , { type: ValidationType.MINCHAR, arguments: ["2"] } + ] + , id: 3 + , sugestions: [] + , subForm: null +}; +/** New form with one less input */ +const form1: Form = { + id: 1 + , title: "Form Title 1" + , description: "Form Description 1" + , inputs: [ + Input1 + , Input2 + ] +}; + +/** New form with one more Input */ +const form2: Form = { + id: 1 + , title: "Form Title 1" + , description: "Form Description 1" + , inputs: [ + Input1 + , Input2 + , Input3 + , Input4 + ] +}; +/** New form with swapped inputs */ +const form3: Form = { + id: 1 + , title: "Form Title 1" + , description: "Form Description 1" + , inputs: [ + Input1Otherplacement + , Input2Placement0 + , Input3 + ] +}; +/** New form with inputs that were removed and added */ +const form4: Form = { + id: 1 + , title: "Form Title 1" + , description: "Form Description 1" + , inputs: [ + Input1UndefinedID + , Input2UndefinedID + , Input3UndefinedID + ] +}; +/** New form resulting from the aplication of all operations */ +const form5: Form = { + id: 1 + , title: "Form Title 1" + , description: "Form Description 1" + , inputs: [ + Input1Otherplacement + , Input2Placement0id2 + , Input3OtherID + , Input4 + ] +}; +/** New form resulting from the restoration of an Input */ +const form6: Form = { + id: 1 + , title: "Form Title 1" + , description: "Form Description 1" + , inputs: [ + Input1 + , Input2Placement2id2 + , Input3Placement1id3 + ] +}; +/** New form */ +const form7: Form = { + id: 1 + , title: "Form Title 1" + , description: "Form Description 1" + , inputs: [ + Input1Empty + , Input2Placement0idNULL + , Input3idNULL + ] +}; +/** New form created with a wrong title */ +const form8: Form = { + id: 1 + , title: "Title 1" + , description: "Description 1" + , inputs: [ + Input1 + , Input2 + ] +}; +/** New form created */ +const formToRead: Form = { + id: 1 + , title: "Title 1" + , description: "Description 1" + , inputs: [ + Input1 + , Input2 + ] +}; +/** Old form that serves as a base for comparison */ +const formBase: Form = { + id: 1 + , title: "Form Title 1" + , description: "Form Description 1" + , inputs: [ + Input1 + , Input2 + , Input3 + ] +}; +/** Another old form that serves as a base for comparison */ +const formBase2: Form = { + id: 1 + , title: "Form Title 1" + , description: "Form Description 1" + , inputs: [ + Input1 + , Input2id2 + , Input4Placement2id4 + ] +}; +/** Another old form that serves as a base for comparison */ +const formBase3: Form = { + id: 1 + , title: "Form Title 1" + , description: "Form Description 1" + , inputs: [ + Input1 + , mixedInput1 + ] +}; +/** Empty form used as a base for comparison */ +const emptyForm: Form = { + id: 1 + , title: "Form Title 1" + , description: "Form Description 1" + , inputs: [] +}; +/** Expected input update to check an form update with a 'remove' type */ +const expInput1: InputUpdate = { + input: Input3 + , inputOperation: UpdateType.REMOVE + , value: null +}; + +/** Expected input update to check an form update with a 'add' type */ +const expInput2: InputUpdate = { + input: Input4 + , inputOperation: UpdateType.ADD + , value: null +}; + +/** Expected input update to check an form update with a 'swap' type */ +const expInput3: InputUpdate = { + input: Input1Otherplacement + , inputOperation: UpdateType.SWAP + , value: "" + 0 +}; + +/** Expected input update to check an form update with a 'swap' type */ +const expInput4: InputUpdate = { + input: Input2Placement0 + , inputOperation: UpdateType.SWAP + , value: "" + 1 +}; +/** Expected input update to check an form update with 'swap' type */ +const expInput5: InputUpdate = { + input: Input1UndefinedID + , inputOperation: UpdateType.ADD + , value: null +}; +/** Expected input update to check an form update with 'add' type */ +const expInput6: InputUpdate = { + input: Input2UndefinedID + , inputOperation: UpdateType.ADD + , value: null +}; +/** Expected input update to check an form update with 'add' type */ +const expInput7: InputUpdate = { + input: Input3UndefinedID + , inputOperation: UpdateType.ADD + , value: null +}; +/** Expected input update to check an form update with 'swap' type */ +const expInput8: InputUpdate = { + input: Input2Placement0id2 + , inputOperation: UpdateType.SWAP + , value: "" + 1 +}; +/** Expected input update to check an form update with 'reenabled' type */ +const expInput9: InputUpdate = { + input: Input3OtherID + , inputOperation: UpdateType.REENABLED + , value: null +}; +/** Expected input update to check an form update with 'remove' type */ +const expInput10: InputUpdate = { + input: Input4Placement2id4 + , inputOperation: UpdateType.REMOVE + , value: null +}; +/** Expected input update to check an form update with 'remove' type */ +const expInput11: InputUpdate = { + input: Input2Placement2id2 + , inputOperation: UpdateType.REENABLED + , value: null +}; +/** Expected input update to check an form update when create a new form */ +const expInput12: InputUpdate = { + input: Input1Empty + , inputOperation: UpdateType.ADD + , value: null +}; +/** Expected input update to check an form update when create a new form */ +const expInput13: InputUpdate = { + input: Input2Placement0idNULL + , inputOperation: UpdateType.ADD + , value: null +}; +/** Expected input update to check an form update when create a new form */ +const expInput14: InputUpdate = { + input: Input3idNULL + , inputOperation: UpdateType.ADD + , value: null +}; +/** Expected input update with "remove" operation type */ +const expInputUsingformBase1: InputUpdate = { + input: Input1 + , inputOperation: UpdateType.REMOVE + , value: null +}; +/** Expected input update */ +const expInputUsingformBase2: InputUpdate = { + input: Input2 + , inputOperation: UpdateType.REMOVE + , value: null +}; +/** Base form used as a correctude parameter */ +const expFormUpdate1: FormUpdate = { + form: form1 + , updateDate: date + , inputUpdates: [ expInput1 ] +}; +/** Base form used as a correctude parameter */ +const expFormUpdate2: FormUpdate = { + form: form2 + , updateDate: date + , inputUpdates: [ expInput2 ] +}; +/** Base form used as a correctude parameter */ +const expFormUpdate3: FormUpdate = { + form: form3 + , updateDate: date + , inputUpdates: [ + expInput3 + , expInput4 + ] +}; +/** Base form used as a correctude parameter */ +const expFormUpdate4: FormUpdate = { + form: form4 + , updateDate: date + , inputUpdates: [ + expInputUsingformBase1 + , expInput1 + , expInputUsingformBase2 + , expInput5 + , expInput6 + , expInput7 + ] +}; +/** Base form used as a correctude parameter */ +const expFormUpdateALL: FormUpdate = { + form: form5 + , updateDate: date + , inputUpdates: [ + expInput3 + , expInput8 + , expInput9 + , expInput10 + , expInput2 + ] +}; +/** Base form used as a correctude parameter */ +const expFormUpdateRESTORE: FormUpdate = { + form: form6 + , updateDate: date + , inputUpdates: [ + expInput11 + ] +}; +/** Base form used as a correctude parameter */ +const expFormUpdateCREATION: FormUpdate = { + form: form7 + , updateDate: date + , inputUpdates: [ + expInput12 + , expInput13 + , expInput14 + ] +}; +/** Base form used as a correctude parameter */ +const expFormUpdateREMOVEALL: FormUpdate = { + form: emptyForm + , updateDate: date + , inputUpdates: [ + expInputUsingformBase1 + , expInput1 + , expInputUsingformBase2 + ] +}; +/** Base form used as a correctude parameter */ +const expFormUpdateTITLE: FormUpdate = { + form: form8 + , updateDate: date + , changed: true + , inputUpdates: [] +}; + +/** ============================================= */ +/** + * Testing inputUpdate.spec.ts -- compare if an input update went fine + */ +/** Options of the input used to update */ +const optstoUpdate: InputUpdateOptions = { + input: Input1 + , inputOperation: UpdateType.ADD + , value: null +}; +/** The proper update */ +const resInputUpdate: InputUpdate = new InputUpdate(optstoUpdate); +/** Input used as comparison at "should create a valid InputUpdate with id null" */ +const expInputUpdate: InputUpdate = { + input: Input1 + , inputOperation: UpdateType.ADD + , value: null + , id: null +}; +/** ============================================= */ +/** Input testing Scenario */ + +/** Options to create an input with undefined Enabled key */ +const inputOptsUndefEnable: InputOptions = { + placement: 0 + , description: "Description Question 1" + , question: "Question 1" + , type: InputType.TEXT + , validation: [] +}; +/** Input created from the Undefined Enabled Key options */ +const inputUndefEnableKey: Input = new Input(inputOptsUndefEnable); +/** + * Options to create an input with null Enabled key + */ +const inputOptsNullEnable: InputOptions = { + placement: 1 + , description: "Description Question 2" + , question: "Question 2" + , enabled: null + , type: InputType.TEXT + , validation: [] +}; +/** Input created from the Null Enabled Key options */ +const inputNullEnabled: Input = new Input(inputOptsNullEnable); +/** Options to create an input with treu Enabled key */ +const inputOptsTrueEnable: InputOptions = { + placement: 1 + , description: "Description Question 2" + , question: "Question 2" + , enabled: true + , type: InputType.TEXT + , validation: [] +}; +/** Input created from the True Enabled Key options */ +const inputTrueEnable: Input = new Input(inputOptsTrueEnable); +/** Options to create an input with false Enabled key */ +const inputOptsFalseEnable: InputOptions = { + placement: 1 + , description: "Description Question 2" + , question: "Question 2" + , enabled: false + , type: InputType.TEXT + , validation: [] +}; +/** Input created from the Flase Enabled Key options */ +const inputFalseEnable: Input = new Input(inputOptsFalseEnable); + +/** ============================================= */ +/** Enum Handler testing Scenario */ +/** Result of stringifying an update, with parameter specifying type 'NONE' */ +const stringifiedUpdateNone = EnumHandler.stringifyUpdateType(UpdateType.NONE); +/** Result of stringifying an update, with parameter specifying type 'ADD' */ +const stringigiedUpdateAdd = EnumHandler.stringifyUpdateType(UpdateType.ADD); +/** Result of stringifying an update, with parameter specifying type 'REMOVE' */ +const stringifiedUpdateRemove = EnumHandler.stringifyUpdateType(UpdateType.REMOVE); +/** Result of stringifying an update, with parameter specifying type 'SWAP' */ +const stringifiedUpdateSwap = EnumHandler.stringifyUpdateType(UpdateType.SWAP); +/** Result of stringifying an update, with parameter specifying type 'REENABLED' */ +const stringifiedUpdateReenabled = EnumHandler.stringifyUpdateType(UpdateType.REENABLED); + +/** Result of parssing an update, with parameter 'add' */ +const parsedUpdateAdd = EnumHandler.parseUpdateType("add"); +/** Result of parssing an update, with parameter 'ADD' */ +const parsedUpdateAddCapitalLetters = EnumHandler.parseUpdateType("ADD"); +/** Result of parssing an update, with parameter 'remove' */ +const parsedUpdateRemove = EnumHandler.parseUpdateType("remove"); +/** Result of parssing an update, with parameter 'REMOVE' */ +const parsedUpdateRemoveCapitalLetters = EnumHandler.parseUpdateType("REMOVE"); +/** Result of parssing an update, with parameter 'swap' */ +const parsedUpdateSwap = EnumHandler.parseUpdateType("swap"); +/** Result of parssing an update, with parameter 'SWAP' */ +const parsedUpdateSwapCapitalLetters = EnumHandler.parseUpdateType("SWAP"); +/** Result of parssing an update, with parameter 'reenabled' */ +const parsedUpdateReenabled = EnumHandler.parseUpdateType("reenabled"); +/** Result of parssing an update, with parameter 'REENABLED' */ +const parsedUpdateReenabledCapitalLetters = EnumHandler.parseUpdateType("REENABLED"); +/** Result of parssing an update, with parameter "" */ +const parsedUpdateNone = EnumHandler.parseUpdateType(""); +/** Result of parssing an update, with parameter 'fool' */ +const parsedUpdateFOOL = EnumHandler.parseUpdateType("fool"); + +/** Result of stringifying an input, with parameter specifying type 'NONE' */ +const stringifiedInputNone = EnumHandler.stringifyInputType(InputType.NONE); +/** Result of stringifying an input, with parameter specifying type 'TEXT' */ +const stringifiedInputText = EnumHandler.stringifyInputType(InputType.TEXT); +/** Result of stringifying an input, with parameter specifying type 'RADIO' */ +const stringifiedInputRadio = EnumHandler.stringifyInputType(InputType.RADIO); +/** Result of stringifying an input, with parameter specifying type 'CHECKBOX' */ +const stringifiedInputCheckbox = EnumHandler.stringifyInputType(InputType.CHECKBOX); + +/** Result of parssing an input, with parameter '' */ +const parsedInputNone = EnumHandler.parseInputType(""); +/** Result of parssing an input, with parameter 'text' */ +const parsedInputText = EnumHandler.parseInputType("text"); +/** Result of parssing an input, with parameter 'TEXT' */ +const parsedInputTextCapitalLetters = EnumHandler.parseInputType("TEXT"); +/** Result of parssing an input, with parameter 'radio' */ +const parsedInputRadio = EnumHandler.parseInputType("radio"); +/** Result of parssing an input, with parameter 'RADIO' */ +const parsedInputRadioCapitalLetters = EnumHandler.parseInputType("RADIO"); +/** Result of parssing an input, with parameter 'checkbox' */ +const parsedInputCheckbox = EnumHandler.parseInputType("checkbox"); +/** Result of parssing an input, with parameter 'CHECKBOX' */ +const parsedInputCheckboxCapitalLetters = EnumHandler.parseInputType("CHECKBOX"); +/** Result of parssing an input, with parameter 'fool' */ +const parsedInputFOOL = EnumHandler.parseInputType("fool"); +/** Result of parssing an input, with parameter 'select' */ +const parsedInputSelect = EnumHandler.parseInputType("select"); +/** Result of parssing an input, with parameter 'SELECT' */ +const parsedInputSelectCapitalLetters = EnumHandler.parseInputType("SELECT"); + +/** Result of stringifying an validation, with parameter specifying type 'REGEX' */ +const stringifiedValidationRegex = EnumHandler.stringifyValidationType(ValidationType.REGEX); +/** Result of stringifying an validation, with parameter specifying type 'MANDATORY' */ +const stringifiedValidationMandatory = EnumHandler.stringifyValidationType(ValidationType.MANDATORY); +/** Result of stringifying an validation, with parameter specifying type 'MAXCHAR' */ +const stringifiedValidationMaxChar = EnumHandler.stringifyValidationType(ValidationType.MAXCHAR); +/** Result of stringifying an validation, with parameter specifying type 'MINCHAR' */ +const stringifiedValidationMinChar = EnumHandler.stringifyValidationType(ValidationType.MINCHAR); +/** Result of stringifying an validation, with parameter specifying type 'TYPEOF' */ +const stringifiedValidationTypeOf = EnumHandler.stringifyValidationType(ValidationType.TYPEOF); +/** Result of stringifying an validation, with parameter specifyingtype 'SOMECHAR' */ +const stringifiedValidationSomeCheckbox = EnumHandler.stringifyValidationType(ValidationType.SOMECHECKBOX); +/** Result of stringifying an validation, with parameter specifying type 'MAXANSWERS' */ +const stringifiedValidationMaxAnswers = EnumHandler.stringifyValidationType(ValidationType.MAXANSWERS); +/** Result of stringifying an validation, with parameter specifying type 'NONE' */ +const stringifiedValidationNone = EnumHandler.stringifyValidationType(ValidationType.NONE); + +/** Result of parssing an validation, with parameter 'regex' */ +const parsedValidationRegex = EnumHandler.parseValidationType("regex"); +/** Result of parssing an validation, with parameter 'REGEX' */ +const parsedValidationRegexCapitalized = EnumHandler.parseValidationType("REGEX"); +/** Result of parssing an validation, with parameter 'mandatory' */ +const parsedValidationMandatory = EnumHandler.parseValidationType("mandatory"); +/** Result of parssing an validation, with parameter 'MANDATORY' */ +const parsedValidationMandatoryCapitalized = EnumHandler.parseValidationType("MANDATORY"); +/** Result of parssing an validation, with parameter 'maxchar' */ +const parsedValidationMaxChar = EnumHandler.parseValidationType("maxchar"); +/** Result of parssing an validation, with parameter 'MAXCHAR' */ +const parsedValidationMaxCharyCapitalized = EnumHandler.parseValidationType("MAXCHAR"); +/** Result of parssing an validation, with parameter 'minchar' */ +const parsedValidationMinChar = EnumHandler.parseValidationType("minchar"); +/** Result of parssing an validation, with parameter 'MINCHAR' */ +const parsedValidationMinCharyCapitalized = EnumHandler.parseValidationType("MINCHAR"); +/** Result of parssing an validation, with parameter 'typeof' */ +const parsedValidationTypeOf = EnumHandler.parseValidationType("typeof"); +/** Result of parssing an validation, with parameter 'TYPEOF' */ +const parsedValidationTypeOfCapitalized = EnumHandler.parseValidationType("TYPEOF"); +/** Result of parssing an validation, with parameter 'somecheckbox' */ +const parsedValidationSomeCheckbox = EnumHandler.parseValidationType("somecheckbox"); +/** Result of parssing an validation, with parameter 'SOMECHECKBOX' */ +const parsedValidationSomeCheckboxCapitalized = EnumHandler.parseValidationType("SOMECHECKBOX"); +/** Result of parssing an validation, with parameter 'maxanswers' */ +const parsedValidationMaxAnswers = EnumHandler.parseValidationType("maxanswers"); +/** Result of parssing an validation, with parameter 'MAXANSWERS' */ +const parsedValidationMaxAnswersCapitalized = EnumHandler.parseValidationType("MAXANSWERS"); +/** Result of parssing an validation, with parameter 'dependency' */ +const parsedValidationDependency = EnumHandler.parseValidationType("dependency"); +/** Result of parssing an validation, with parameter 'DEPENDENCY' */ +const parsedValidationDependencyCapitalized = EnumHandler.parseValidationType("DEPENDENCY"); +/** Result of parssing an validation, with parameter '' */ +const parsedValidationNone = EnumHandler.parseValidationType(""); +/** Result of parssing an validation, with parameter 'fool' */ +const parsedValidatioFOOL = EnumHandler.parseValidationType("fool"); + +/** ============================================= */ +/** formUpdate testing Scenario */ +/** Input Opts to be used as input parameters */ +const inputOptsFull: InputOptions = { + placement: 0 + , description: "Description Question 1 Form 1" + , question: "Question 1 Form 1" + , enabled: true + , type: InputType.TEXT + , validation: [] + , id: 1 +}; + +/** Input update options to be used as inputUpdateOpts parameter */ +const inputUpdateObj: InputUpdateOptions = { + input: inputOptsFull + , inputOperation: UpdateType.ADD + , value: null +}; +/** FormOpts that will be used on a update as a parameter */ +const formOptsObj: FormOptions = { + id: 1 + , title: "Form Title 1" + , description: "Form Description 1" + , inputs: [ inputOptsFull ] +}; +/** FormUpdate used to test the case where it has null id */ +const formUpdateOptsObj: FormUpdateOptions = { + form: formOptsObj + , updateDate: new Date() + , inputUpdates: [ inputUpdateObj ] +}; +/** FormUpdate that will be compared expecting to have null id */ +const formUpdateNullId: FormUpdate = new FormUpdate(formUpdateOptsObj); + +/** Expected form with null id */ +const expFormtoUpdateNullId: Form = { + id: 1 + , title: "Form Title 1" + , description: "Form Description 1" + , inputs: [ Input1 ] +}; +/** Expected form update having null id */ +const expFormUpdate: FormUpdate = { + form: expFormtoUpdateNullId + , updateDate: new Date() + , inputUpdates: [ expInputUpdate] + , id: null +}; +/** ============================================= */ + +/** optHandler testing Scenario */ + +/** InputOpts used to create a form to be used to test its atributes */ +const optsInput1: InputOptions = { // equivalente à input 1 + placement: 0 + , description: "Description Question 1 Form 1" + , question: "Question 1 Form 1" + , type: InputType.TEXT + , validation: [] + , id: 1 +}; +/** InputOpts used to create a form to be used to test its atributes */ +const optsInput2: InputOptions = { // equivalente à Input2 + placement: 1 + , description: "Description Question 2 Form 1" + , question: "Question 2 Form 1" + , type: InputType.TEXT + , validation: [ + { type: ValidationType.MAXCHAR, arguments: ["10"] } + , { type: ValidationType.MINCHAR, arguments: ["2"] } + ] + , id: 3 +}; +/** InputOpts used to create a form to be used to test its atributes */ +const optsInput3: InputOptions = { // equivalente à Input3 + placement: 2 + , description: "Description Question 3 Form 1" + , question: "Question 3 Form 1" + , type: InputType.TEXT + , validation: [ + { type: ValidationType.REGEX, arguments: ["\\d{5}-\\d{3}"] } + , { type: ValidationType.MANDATORY, arguments: [] } + ] + , id: 2 +}; +/** InputOpts used to create a InputUpdate */ +const inputOpts4: InputOptions = { + placement: 1 + , description: "Description Question 2 Form 1" + , question: "Question 2 Form 1" + , type: InputType.TEXT + , validation: [] + , id: 1 +}; +/** InputOpts that has a sugestion */ +const inputOptsSugestionMissingPlacement: any = { + placement: 1 + , description: "Description Question 2 Form 1" + , question: "Question 2 Form 1" + , type: InputType.TEXT + , validation: [] + , sugestions: [ + { value: "Malformed Sugestion" } + , { value: "Sugestion", placement: 0 } + ] + , id: 1 +}; +/** InputOpts with a sugestion that misses value */ +const inputOptsSugestionMissingValue: any = { + placement: 1 + , description: "Description Question 2 Form 1" + , question: "Question 2 Form 1" + , type: InputType.TEXT + , validation: [] + , sugestions: [ + { placement: 0} + , { value: "Sugestion", placement: 1 } + ] + , id: 1 +}; +/** Form that has no title atribute */ +const formMissingTitle: any = { + id: 1 + , description: "Form Description 1" + , inputs: [ + OptHandler.input(optsInput1) + , OptHandler.input(optsInput2) + , OptHandler.input(optsInput3) + ] +}; +/** Form that has no description atribute */ +const formMissingDescription: any = { + id: 1 + , title: "Form Title 1" + , inputs: [ + OptHandler.input(optsInput1) + , OptHandler.input(optsInput2) + , OptHandler.input(optsInput3) + ] +}; +/** Form that has no input atribute */ +const formMissingInputs: any = { + id: 1 + , title: "Form Title 1" + , description: "Form Description 1" +}; +/** Input that has no placement atribute */ +const inputMissingPlacement: any = { + description: "Description Question 1 Form 1" + , question: "Question 1 Form 1" + , type: InputType.TEXT + , validation: [] + , inputs: "Fool" + , id: 1 +}; +/** Input that has no description atribute */ +const inputMissingDescription: any = { + placement: 0 + , question: "Question 1 Form 1" + , type: InputType.TEXT + , validation: [] + , id: 1 +}; +/** Input that has no question atribute */ +const inputMissingQuestion: any = { + placement: 0 + , description: "Description Question 1 Form 1" + , type: InputType.TEXT + , validation: [] + , id: 1 +}; +/** Input that has no type atribute */ +const inputMissingType: any = { + placement: 0 + , description: "Description Question 1 Form 1" + , question: "Question 1 Form 1" + , validation: [] + , id: 1 +}; +/** Input that has no validation atribute */ +const inputMissingValidation: any = { + placement: 0 + , description: "Description Question 1 Form 1" + , question: "Question 1 Form 1" + , type: InputType.TEXT + , id: 1 +}; +/** Input that has a non-array validation atribute */ +const inputValidationNotAnArray: any = { + placement: 0 + , description: "Description Question 1 Form 1" + , question: "Question 1 Form 1" + , type: InputType.TEXT + , validation: "fool" + , id: 1 +}; +/** Correct input options that will be used on dictionaries */ +const inputAnswerOpts1: InputAnswerOptions = { + id: 1 + , idInput: null + , placement: 0 + , value: "Answer 1 to Question 1 Form 1" +}; +/** Correct input options that will be used on dictionaries */ +const inputAnswerOpts2: InputAnswerOptions = { + id: 2 + , idInput: null + , placement: 0 + , value: "Answer 1 to Question 2 Form" +}; +/** Correct input options that will be used on dictionaries */ +const inputAnswerOpts3: InputAnswerOptions = { + id: 3 + , idInput: null + , placement: 0 + , value: "Answer 1 to Question 3 Form" +}; +/** Correct input options that will be used on dictionaries */ +const inputAnswerOpts4: InputAnswerOptions = { + id: 4 + , idInput: null + , placement: 1 + , value: "Answer 2 to Question 3 Form" +}; +/** InputOpt missing id atribute - should return error */ +const inputOptsMissingId: any = { + id: 1 + , placement: 0 + , value: "Answer 1 to Question 1 Form 1" +}; +/** InputOpt missing placement atribute */ +const inputOptsMissingPlacement: any = { + id: 1 + , idInput: null + , value: "Answer 1 to Question 1 Form 1" +}; +/** InputOpt missing value property */ +const inputOptsMissingValue: any = { + id: 1 + , idInput: null + , placement: 0 +}; +/** Dictionary containing valid inputOpts */ +const inputAnswerOptionsDictOptHandler: InputAnswerOptionsDict = { + 1: [inputAnswerOpts1] + , 2: [inputAnswerOpts2] + , 3: [inputAnswerOpts3, inputAnswerOpts4] +}; +/** Dictionary containing non-valid inputOpts that has no IdInput */ +const dictMissingIdInput: InputAnswerOptionsDict = { + 1: [inputOptsMissingId] +}; +/** Dictionary containing non-valid inputOpts that has no placment */ +const dictMissingPlacement: InputAnswerOptionsDict = { + 1: [inputOptsMissingPlacement] +}; +/** Dictionary contaunung non-valid inputOpts that has no value property */ +const dictMissingValue: InputAnswerOptionsDict = { + 1: [inputOptsMissingValue] +}; + +/** FormAnswer that has no form associated */ +const formAnswerHasNoFormAssociated: any = { + id: 1 + , timestamp: date + , inputsAnswerOptions: inputAnswerOptionsDictOptHandler +}; +/** FormAnswer that has no form type associated */ +const formAnswerHasNoFormType: any = { + id: 1 + , form: inputOpts4 + , timestamp: date + , inputsAnswerOptions: inputAnswerOptionsDictOptHandler +}; +/** FormOpts used to create a form with valid porperties */ +const fullFormOptions: FormOptions = { + id: 1 + , title: "Form Title 1" + , description: "Form Description 1" + , inputs: [ + OptHandler.input(optsInput1) + , OptHandler.input(optsInput2) + , OptHandler.input(optsInput3) + ] +}; +const formForAnswer: Form = { + id: 1 + , title: "Form Title 1" + , description: "Form Description 1" + , inputs: [ + Input1 + , Input2 + , Input3 + ] +}; +/** Valid form created used fullFormOptions */ +const tmpForm: Form = new Form(OptHandler.form(fullFormOptions)); + +/** Form missing date property */ +const formHasNoDate: any = { + id: 1 + , form: tmpForm + , inputsAnswerOptions: inputAnswerOptionsDictOptHandler +}; +/** Form with invalid date property */ +const formHasNoDateType: any = { + id: 1 + , form: tmpForm + , timestamp: "data" + , inputsAnswerOptions: inputAnswerOptionsDictOptHandler +}; +/** Form missing answers dictionary */ +const formHasNoDict: any = { + id: 1 + , form: tmpForm + , timestamp: date +}; +/** Form with input missing it's id */ +const formMissingInputOptIdInput: FormAnswerOptions = { + id: 1 + , form: tmpForm + , timestamp: date + , inputsAnswerOptions: dictMissingIdInput +}; +/** Form with input missing it's placement */ +const formMissingInputOptPlacement: FormAnswerOptions = { + id: 1 + , form: tmpForm + , timestamp: date + , inputsAnswerOptions: dictMissingPlacement +}; +/** Form with input missing it's value property */ +const formMissingInputOptValue: FormAnswerOptions = { + id: 1 + , form: tmpForm + , timestamp: date + , inputsAnswerOptions: dictMissingValue +}; +/** Correct form */ +const validForm: any = { + id: 1 + , form: tmpForm + , timestamp: date + , inputsAnswerOptions: inputAnswerOptionsDictOptHandler +}; +/** Valid inputUpdate object, remove type */ +const updateObj1: InputUpdateOptions = { + id: 1 + , input: optsInput1 + , inputOperation: UpdateType.REMOVE + , value: null +}; +/** Valid inputUpdate object, remove type */ +const updateObj2: InputUpdateOptions = { + id: 2 + , input: optsInput2 + , inputOperation: UpdateType.REMOVE + , value: null +}; +/** Valid inputUpdate object, remove type */ +const updateObj3: InputUpdateOptions = { + id: 3 + , input: optsInput3 + , inputOperation: UpdateType.REMOVE + , value: null +}; +/** Valid formUpdate object, remove type */ +const validFormUpdateObj: any = { + id: 1 + , form: fullFormOptions + , updateDate: date + , inputUpdates: [ + updateObj1 + , updateObj2 + , updateObj3 + ] +}; +/** FormUpdate with non-array inputUpdates */ +const formUpdateNotArrayInputUpdates: any = { + id: 1 + , form: fullFormOptions + , updateDate: date + , inputUpdates: updateObj1 +}; +/** FormUpdate object missing the form to update */ +const formUpdateMissingForm: any = { + id: 1 + , updateDate: date + , inputUpdates: [ + updateObj1 + , updateObj2 + , updateObj3 + ] +}; +/** FormUpdate missing the inputs to update */ +const formUpdateMissinginputUpdate: any = { + id: 1 + , form: fullFormOptions + , updateDate: date +}; +/** InputUpdate with undefined input property and null value */ +const inputUpdateUndefinedInput: any = { + id: 1 + , input: undefined + , inputOperation: UpdateType.REMOVE + , value: null +}; +/** InputUpdate missing the inputOperation */ +const inputUpdateMissingInputOperation: any = { + id: 1 + , input: inputOpts4 + , value: "Remove question 3 from form 1" +}; +/** InputUpdate missing value */ +const inputUpdateMissingValue: any = { + id: 1 + , input: inputOpts4 + , inputOperation: UpdateType.REMOVE +}; + +/** Valid SubForm. */ +const subFormOpts1: SubFormOptions = { + id: 1 + , inputId: null + , contentFormId: 2 +}; +/** Input with SUBFORM type with a valid SubForm. */ +const inputWithValidSubForm: any = { + placement: 1 + , description: "Description" + , question: "Question" + , type: InputType.SUBFORM + , validation: [] + , sugestions: [] + , subForm: subFormOpts1 +}; +/** Input with SUBFORM type without a SubForm. */ +const inputWithoutSubForm: any = { + placement: 1 + , description: "Description" + , question: "Question" + , type: InputType.SUBFORM + , validation: [] + , sugestions: [] + , subForm: null +}; + +/** + * SubForms + */ +/** A invalid SubForm object missing contentFormId property. */ +const malformedSubForm1: any = { + id: 1 + , inputId: 30 +}; +/** A invalid SubForm object missing contentFormId property. */ +const malformedSubForm2: any = { + id: 1 + , contentFormId: 190 +}; +/** A valid SubForm object. */ +const subFormObj1: SubFormOptions = { + id: 1 + , inputId: 30 + , contentFormId: 1 +}; +/** A valid SubForm object. */ +const subFormObj2: SubFormOptions = { + id: 2 + , inputId: 32 + , contentFormId: 6 +}; +/** A valid SubForm object. */ +const subFormObj3: SubFormOptions = { + id: 3 + , inputId: 32 + , contentFormId: 20 +}; +/** A valid SubForm object. */ +const subFormObj4: SubFormOptions = { + id: 3 + , inputId: 33 + , contentFormId: 2 +}; +/** A valid SubForm object. */ +const subFormObj5: SubFormOptions = { + id: 3 + , inputId: 35 + , contentFormId: 9 +}; +/** A valid SubForm object. */ +const subFormObj6: SubFormOptions = { + id: 3 + , inputId: 36 + , contentFormId: 8 +}; +/** A valid SubForm object. */ +const subFormObj7: SubFormOptions = { + id: 4 + , inputId: 38 + , contentFormId: 11 +}; + +/** Input with SUBFORM type with a malformed SubForm. */ +const inputWithMalformedSubForm1: any = { + placement: 1 + , description: "Description" + , question: "Question" + , type: InputType.SUBFORM + , validation: [] + , sugestions: [] + , subForm: malformedSubForm1 +}; + +/** Input with SUBFORM type with a malformed SubForm. */ +const inputWithMalformedSubForm2: any = { + placement: 2 + , description: "Description" + , question: "Question" + , type: InputType.SUBFORM + , validation: [] + , sugestions: [] + , subForm: malformedSubForm2 +}; + +/** A input with SUBFORM type with a valid SubForm. */ +const inputOptWithValidSubForm1: InputOptions = { + placement: 0 + , description: "Description Question 1 Form 8" + , question: "Question 1 Form 8" + , enabled: true + , type: InputType.SUBFORM + , validation: [] + , sugestions: [] + , id: 30 + , subForm: subFormObj1 +}; +/** A input with SUBFORM type with a valid SubForm. */ +const inputOptWithValidSubForm2: InputOptions = { + placement: 0 + , description: "Description Question 1 Form 8" + , question: "Question 1 Form 8" + , enabled: true + , type: InputType.SUBFORM + , sugestions: [] + , validation: [] + , id: 32 + , subForm: subFormObj2 +}; +/** A input with SUBFORM type with a valid SubForm. */ +const inputOptWithValidSubForm3: InputOptions = { + placement: 1 + , description: "Description Question 1 Form 8" + , question: "Question 1 Form 8" + , enabled: true + , type: InputType.SUBFORM + , sugestions: [] + , validation: [] + , id: 32 + , subForm: subFormObj2 +}; +/** A input with SUBFORM type with a valid SubForm. */ +const inputOptWithValidSubForm4: InputOptions = { + placement: 2 + , description: "Description Question 3 Form 8" + , question: "Question 3 Form 8" + , enabled: true + , type: InputType.SUBFORM + , sugestions: [] + , validation: [] + , id: 35 + , subForm: subFormObj6 +}; +/** A input with SUBFORM type with a valid SubForm. */ +const inputOptWithValidSubForm5: InputOptions = { + placement: 0 + , description: "Description Question 1 Form 11" + , question: "Question 1 Form 11" + , enabled: true + , type: InputType.SUBFORM + , sugestions: [] + , validation: [] + , id: 36 + , subForm: subFormObj6 +}; +/** A input with SUBFORM type with a valid SubForm. */ +const inputOptWithValidSubForm6: InputOptions = { + placement: 2 + , description: "Description Question 3 Form 8" + , question: "Question 3 Form 8" + , enabled: true + , type: InputType.SUBFORM + , sugestions: [] + , validation: [] + , id: 38 + , subForm: subFormObj7 +}; +/** A simple input for the form with id 8. */ +const inputOpt1ForForm8: InputOptions = { + placement: 1 + , description: "Description Question 2 Form 8" + , question: "Question 2 Form 8" + , enabled: true + , type: InputType.TEXT + , validation: [] + , sugestions: [] + , id: 31 + , subForm: null +}; +/** A simple input for the form with id 8. */ +const inputOpt2ForForm8: InputOptions = { + placement: 1 + , description: "Description Question 2 Form 8" + , question: "Question 2 Form 8" + , enabled: true + , type: InputType.TEXT + , sugestions: [] + , validation: [] + , id: 33 + , subForm: null +}; +/** A simple input for the form with id 8. */ +const inputOpt3ForForm8: InputOptions = { + placement: 0 + , description: "Description Question 2 Form 8" + , question: "Question 2 Form 8" + , enabled: true + , type: InputType.TEXT + , sugestions: [] + , validation: [] + , id: 33 + , subForm: null +}; +/** A simple input for the form with id 9. */ +const inputOpt1ForForm9: InputOptions = { + placement: 1 + , description: "Description Question 2 Form 9" + , question: "Question 2 Form 9" + , enabled: true + , type: InputType.TEXT + , sugestions: [] + , validation: [] + , id: 35 +}; +/** A simple input for the form with id 11. */ +const inputOpt1ForForm11: InputOptions = { + placement: 1 + , description: "Description Question 2 Form 11" + , question: "Question 2 Form 11" + , enabled: true + , type: InputType.TEXT + , sugestions: [] + , validation: [] + , id: 37 +}; +/** A input with a invalid SubForm */ +const inputOptWithInvalidSubForm1: InputOptions = { + placement: 0 + , description: "Description Question 1 Form 9" + , question: "Question 1 Form 9" + , enabled: true + , type: InputType.SUBFORM + , sugestions: [] + , validation: [] + , id: 36 + , subForm: subFormObj3 +}; +/** A simple input for the form with id 8. */ +const inputOptWithInvalidSubForm2: InputOptions = { + placement: 0 + , description: "Description Question 1 Form 9" + , question: "Question 1 Form 9" + , enabled: true + , type: InputType.SUBFORM + , sugestions: [] + , validation: [] + , id: 38 + , subForm: subFormObj5 +}; +/** A input with a invalid SubForm */ +const inputOptWithInvalidSubForm3: InputOptions = { + placement: 2 + , description: "Description Question 4 Form 8" + , question: "Question 4 Form 8" + , enabled: true + , type: InputType.SUBFORM + , sugestions: [] + , validation: [] + , id: 36 + , subForm: subFormObj6 +}; +/** A input with a invalid SubForm */ +const inputOptWithInvalidSubForm4: InputOptions = { + placement: 2 + , description: "Description Question 4 Form 8" + , question: "Question 4 Form 8" + , enabled: true + , type: InputType.SUBFORM + , sugestions: [] + , validation: [] + , id: 36 + , subForm: subFormObj7 +}; + +/** A form with a valid SubForm */ +const formWithValidSubForm1: FormOptions = { + id: 8 + , title: "Form Title 8" + , description: "Form Description 8" + , inputs: [ + new Input(inputOptWithValidSubForm1) + , new Input(inputOpt1ForForm8) + ] +}; +/** A form with a valid SubForm */ +const formWithValidSubForm2: FormOptions = { + id: 11 + , title: "Form Title 11" + , description: "Form Description 11" + , inputs: [ + new Input(inputOptWithValidSubForm5) + , new Input(inputOpt1ForForm11) + ] +}; +/** A form with a invalid SubForm */ +const formWithInvalidSubForm1: FormOptions = { + id: 9 + , title: "Form Title 9" + , description: "Form Description 9" + , inputs: [ + inputOptWithInvalidSubForm1 + , inputOpt1ForForm9 + ] +}; + +/** A form with a invalid SubForm */ +const formWithInvalidSubForm2: FormOptions = { + id: 9 + , title: "Form Title 9" + , description: "Form Description 9" + , inputs: [ + inputOptWithInvalidSubForm2 + , inputOpt1ForForm9 + ] +}; + +/** A updated version of form 8 */ +const updatedFormWithValidSubForm1: FormOptions = { + id: 8 + , title: "Form Title 8" + , description: "Form Description 8" + , inputs: [ + inputOptWithValidSubForm2 + , inputOpt2ForForm8 + ] +}; +/** A updated version of form 8 */ +const updatedFormWithValidSubForm2: FormOptions = { + id: 8 + , title: "Form Title 8" + , description: "Form Description 8" + , inputs: [ + inputOpt3ForForm8 + , inputOptWithValidSubForm3 + ] +}; + +/** A invalid updated version of form 8 */ +const updatedFormWithInvalidSubForm1: FormOptions = { + id: 8 + , title: "Form Title 8" + , description: "Form Description 8" + , inputs: [ + inputOpt3ForForm8 + , inputOptWithValidSubForm3 + , inputOptWithInvalidSubForm4 + ] +}; + +/** InputUpdateOptions with subForms */ +const updateOptForFormWithSubForm1: InputUpdateOptions = { + id: 25 + , input: inputOptWithValidSubForm1 + , inputOperation: UpdateType.REMOVE + , value: null +}; +/** InputUpdateOptions with subForms */ +const updateOptForFormWithSubForm2: InputUpdateOptions = { + id: 26 + , input: inputOpt1ForForm8 + , inputOperation: UpdateType.REMOVE + , value: null +}; +/** InputUpdateOptions with subForms */ +const updateOptForFormWithSubForm3: InputUpdateOptions = { + id: 27 + , input: inputOptWithValidSubForm2 + , inputOperation: UpdateType.ADD + , value: null +}; +/** InputUpdateOptions with subForms */ +const updateOptForFormWithSubForm4: InputUpdateOptions = { + id: 28 + , input: inputOpt2ForForm8 + , inputOperation: UpdateType.ADD + , value: null +}; +/** InputUpdateOptions with subForms */ +const updateOptForFormWithSubForm5: InputUpdateOptions = { + id: 29 + , input: inputOptWithValidSubForm3 + , inputOperation: UpdateType.SWAP + , value: "0" +}; +/** InputUpdateOptions with subForms */ +const updateOptForFormWithSubForm6: InputUpdateOptions = { + id: 30 + , input: inputOpt3ForForm8 + , inputOperation: UpdateType.SWAP + , value: "1" +}; +/** InputUpdateOptions with subForms */ +const updateOptForFormWithSubForm7: InputUpdateOptions = { + input: inputOptWithValidSubForm1 + , inputOperation: UpdateType.REMOVE + , value: null +}; +/** InputUpdateOptions with subForms */ +const updateOptForFormWithSubForm8: InputUpdateOptions = { + input: inputOptWithValidSubForm4 + , inputOperation: UpdateType.ADD + , value: null +}; +/** InputUpdateOptions with subForms */ +const updateOptForFormWithSubForm9: InputUpdateOptions = { + input: inputOptWithInvalidSubForm3 + , inputOperation: UpdateType.ADD + , value: null +}; +/** InputUpdateOptions with subForms */ +const updateOptForFormWithSubForm10: InputUpdateOptions = { + input: inputOptWithValidSubForm3 + , inputOperation: UpdateType.REMOVE + , value: null +}; +/** InputUpdateOptions with subForms */ +const updateOptForFormWithSubForm11: InputUpdateOptions = { + input: inputOptWithInvalidSubForm4 + , inputOperation: UpdateType.ADD + , value: null +}; + +/** FormUpdateOptions with subForms */ +const formUpdateWithSubForm: FormUpdateOptions = { + id: 11 + , form: updatedFormWithValidSubForm2 + , updateDate: new Date("1999-04-22 11:32:56") + , changed: false + , inputUpdates: [ + updateOptForFormWithSubForm1 + , updateOptForFormWithSubForm2 + , updateOptForFormWithSubForm3 + , updateOptForFormWithSubForm4 + ] +}; +/** FormUpdateOptions with subForms */ +const formUpdateWithSubForm1: FormUpdateOptions = { + id: 12 + , form: updatedFormWithValidSubForm1 + , updateDate: new Date("1999-04-22 11:32:56") + , changed: false + , inputUpdates: [ + updateOptForFormWithSubForm5 + , updateOptForFormWithSubForm6 + ] +}; +/** FormUpdateOptions with subForms */ +const formUpdateWithSubForm2: FormUpdateOptions = { + id: 13 + , form: updatedFormWithValidSubForm1 + , updateDate: new Date("1999-04-22 11:32:56") + , changed: false + , inputUpdates: [ + updateOptForFormWithSubForm7 + , updateOptForFormWithSubForm8 + ] +}; +/** FormUpdateOptions with subForms */ +const formUpdateWithSubForm3: FormUpdateOptions = { + id: 14 + , form: updatedFormWithValidSubForm1 + , updateDate: new Date("1999-04-22 11:32:56") + , changed: false + , inputUpdates: [ + updateOptForFormWithSubForm9 + , updateOptForFormWithSubForm10 + ] +}; +/** FormUpdateOptions with subForms */ +const formUpdateWithSubForm4: FormUpdateOptions = { + id: 15 + , form: updatedFormWithInvalidSubForm1 + , updateDate: new Date("1999-04-22 11:32:56") + , changed: false + , inputUpdates: [ + updateOptForFormWithSubForm11 + ] +}; +/** ============================================= */ + +/** dbHandler testing Scenario */ + +const dateDBH: Date = new Date("2019-02-21 12:10:25"); + +/** QueryString obj to be used on a query to insert a form obj */ +const queryStringInsertFormid5: string = "INSERT INTO form(id, title, description)\ + VALUES (5, 'Form Title 5', 'Form Description 5');"; +/** Query obj to be used to insert a form Obj */ +const queryToInsertFormid5: QueryOptions = {query: queryStringInsertFormid5, parameters: []}; +/** QueryString obj to be used on a query to insert another form Obj */ +const queryStringInsertFormid6: string = "INSERT INTO form(id, title, description)\ +VALUES (6, 'Form Title 6', 'Form Description 6');"; +/** Query obj to be used to insert another form Obj */ +const queryToInsertFormid6: QueryOptions = {query: queryStringInsertFormid6, parameters: []}; +/** QueryString obj to be used on a query to select all inserted form Objs */ +const queryStringSelectAllForms: string = "SELECT * FROM form;"; +/** Query obj to be used to select all inserted form Obj */ +const queryToSelectAllForms: QueryOptions = {query: queryStringSelectAllForms, parameters: []}; +/** QueryString obj to be used on a query to delete a form Obj */ +const queryStringDeleteFormid6: string = "DELETE FROM form WHERE id=6;"; +/** Query obj to be used to delete a form Obj */ +const queryToDeleteFormid6: QueryOptions = {query: queryStringDeleteFormid6, parameters: []}; +/** QueryString obj to be used on a query to delete a form Obj */ +const queryStringDeleteFormid5: string = "DELETE FROM form WHERE id=5;"; +/** Query obj to be used to delete a form Obj */ +const queryToDeleteFormid5: QueryOptions = {query: queryStringDeleteFormid5, parameters: []}; +/** QueryString obj to be used on a query to insert 2 Input Obj */ +const queryStringInsertTwoInputs: string = "INSERT INTO input(id_form, placement, input_type, enabled, question, description)\ + VALUES\ + (2, 3,'TEXT', TRUE, 'Question 3 Form 2', 'Description Question 3 Form 2'),\ + (2, 4,'TEXT', TRUE, 'Question 4 Form 2', 'Description Question 4 Form 2');"; +/** Query obj to be used to insert 2 input Obj */ +const queryToInsertTwoInputs: QueryOptions = {query: queryStringInsertTwoInputs, parameters: []}; +/** QueryString obj to be used on a query to select all inserted Input Obj */ +const queryStringSelectAllInputs: string = "SELECT * FROM input;"; +/** Query obj to be used to select all inserted input Obj */ +const queryToSelectAllInputs: QueryOptions = {query: queryStringSelectAllInputs, parameters: []}; +/** QueryString obj to be used on a query to try to delete a non-existent input */ +const queryStringDeleteNonExistentInput: string = "DELETE FROM input WHERE id=20;"; +/** Query obj to be used to try to delete a non-existent input Obj */ +const queryToDeleteNonExistentInput: QueryOptions = {query: queryStringDeleteNonExistentInput, parameters: []}; + +/** QueryString obj to be used on a query to remove both previously inserted Input Obj */ +const queryStringRemoveBothInputs: string = "DELETE FROM input WHERE id=9 OR id=14 OR id=15;"; +/** Query obj to be used to remove the previosly inserted input Obj */ +const queryToRemoveBothInputs: QueryOptions = {query: queryStringRemoveBothInputs, parameters: []}; +/** QueryString obj to be used on a query to insert 2 InputValidation Obj */ +const queryStringInsertTwoInputVal: string = "INSERT INTO input_validation(id_input, validation_type)\ + VALUES\ + (2, 'MAXCHAR'),\ + (5, 'MANDATORY');"; + +/** Query obj to be used to insert 2 input Value Obj */ +const queryToInsertTwoInputVal: QueryOptions = {query: queryStringInsertTwoInputVal, parameters: []}; +/** QueryString obj to be used on a query to select all inserted InputValidation Obj */ +const queryStringSelectAllInputVal: string = "SELECT * FROM input_validation;"; +/** Query obj to be used to select all inserted input Value Obj */ +const queryToSelectAllInputVal: QueryOptions = {query: queryStringSelectAllInputVal, parameters: []}; +/** QueryString obj to be used on a query to try delete a non-existent InputValidation Obj */ +const queryStringRemoveNEInputVal: string = "DELETE FROM input_validation WHERE id=21;"; +/** Query obj to be used to try to delete a non-existent input Value Obj */ +const queryToRemoveNEInputVal: QueryOptions = {query: queryStringRemoveNEInputVal, parameters: []}; +/** QueryString obj to be used on a query to remove both previously inserted InputValidation Obj */ +const queryStringRemoveInputVal: string = "DELETE FROM input_validation WHERE id=9 OR id=10 OR id=13 OR id=14 OR id=17;"; +/** Query obj to be used to try to delete the previously inserted input Value Obj */ +const queryToRemoveInputVal: QueryOptions = {query: queryStringRemoveInputVal, parameters: []}; +/** QueryString obj to be used on a query to insert a InputValidationArguments Obj */ +const queryStringInsertInputValArgs: string = "INSERT INTO input_validation_argument(id_input_validation, placement, argument)\ +VALUES\ +(1, 2, '10'),\ +(2, 2, '2');"; +/** Query obj to be used to insert input Value Arguments Obj */ +const queryToInsertInputValArgs: QueryOptions = {query: queryStringInsertInputValArgs, parameters: []}; +/** QueryString obj to be used on a query to select all inserted InputValidationArguments Obj */ +const queryStringSelectAllInputValArgs: string = "SELECT * FROM input_validation_argument;"; +/** Query obj to be used to all inserted input Value Arguments Obj */ +const queryToSelectAllInputValArgs: QueryOptions = {query: queryStringSelectAllInputValArgs, parameters: []}; +/** QueryString obj to be used on a query to try to delete a non-existent InputValidationArguments Obj */ +const queryStringRemoveNEtInputValArgs: string = "DELETE FROM input_validation_argument WHERE id=15;"; +/** Query obj to be used to try to remove a non-existent input Value Arguments Obj */ +const queryToRemoveNEInputValArgs: QueryOptions = {query: queryStringRemoveNEtInputValArgs, parameters: []}; +/** QueryString obj to be used on a query to delete the previously inserted InputValidationArguments Obj */ +const queryStringRemoveInputValArgs: string = "DELETE FROM input_validation_argument WHERE id=6;"; +/** Query obj to be used to remove previosly inserted input Value Arguments Obj */ +const queryToRemoveInputValArgs: QueryOptions = {query: queryStringRemoveInputValArgs, parameters: []}; +/** QueryString obj to be used on a query to insert 2 formAnswers Obj */ +const queryStringInsertFormAnswers: string = "INSERT INTO form_answer(id ,id_form, answered_at)\ + VALUES\ + (8, 2, '2018-07-02 10:10:25-03'),\ + (9, 3, '2018-06-03 10:11:25-03');"; +/** Query obj to be used to insert formAnswers Obj */ +const queryToInsertFormAnswers: QueryOptions = {query: queryStringInsertFormAnswers, parameters: []}; +/** QueryString obj to be used on a query to select all inserted formAnswers Obj */ +const queryStringSelectAllFormAnswers: string = "SELECT * FROM form_answer;"; +/** Query obj to be used to select all formAnswers Obj */ +const queryToSelectAllFormAnswers: QueryOptions = {query: queryStringSelectAllFormAnswers, parameters: []}; + +/** QueryString obj to be used on a query to try to delete a non-existent formAnswers Obj */ +const queryStringRemoveNEFormAnswers: string = "DELETE FROM form_answer WHERE id=11;"; +/** Query obj to be used to try to remove a non-existent formAnswers Obj */ +const queryToRemoveNEFormAnswers: QueryOptions = {query: queryStringRemoveNEFormAnswers, parameters: []}; +/** QueryString obj to be used on a query to delete both previously inserted formAnswers Obj */ +const queryStringRemoveFormAnswers: string = "DELETE FROM form_answer WHERE id=8 OR id=9;"; +/** Query obj to be used to remove the previously inserted formAnswers Obj */ +const queryToRemoveFormAnswers: QueryOptions = {query: queryStringRemoveFormAnswers, parameters: []}; +/** QueryString obj to be used on a query to insert 2 inputAnswers Obj */ +const queryStringInsertInputAnswers: string = "INSERT INTO input_answer(id, id_form_answer, id_input, value, placement)\ + VALUES\ + (18,1, 6,'Answer to Question 1 Form 3',1),\ + (19,1, 7,'Answer to Question 2 Form 3',2);"; +/** Query obj to be used to insert inputAnswers Obj */ +const queryToInsertInputAnswers: QueryOptions = {query: queryStringInsertInputAnswers, parameters: []}; +/** QueryString obj to be used on a query to select all inserted inputAnswers Obj */ +const queryStringSelectInputAnswers: string = "SELECT * FROM input_answer;"; +/** Query obj to be used to select all inputAnswers Obj */ +const queryToSelectInputAnswers: QueryOptions = {query: queryStringSelectInputAnswers, parameters: []}; +/** QueryString obj to be used on a query to try to delete a non-existent inputAnswers Obj */ +const queryStringRemoveNEInputAnswers: string = "DELETE FROM input_answer WHERE id=25;"; +/** Query obj to be used to try to remove a non-existent inputAnswers Obj */ +const queryToRemoveNEInputAnswers: QueryOptions = {query: queryStringRemoveNEInputAnswers, parameters: []}; +/** QueryString obj to be used on a query to delete both previously inserted inputAnswers Obj */ +const queryStringRemoveInputAnswers: string = "DELETE FROM input_answer WHERE id=18 OR id=19;"; +/** Query obj to be used to remove the previously inserted inputAnswers Obj */ +const queryToRemoveInputAnswers: QueryOptions = {query: queryStringRemoveInputAnswers, parameters: []}; + +/** 2nd describe */ +/** Input Options object to be used on a formOptions */ +const inputOptsDBH1: InputOptions = { + placement: 0 + , description: "Description Question 1 Form 1" + , question: "Question 1 Form 1" + , type: InputType.TEXT + , validation: [] + , sugestions: [] + , id: 1 +}; +/** Input Options object to be used on a formOptions */ +const inputOptsDBH2: InputOptions = { + placement: 1 + , description: "Description Question 3 Form 1" + , question: "Question 3 Form 1" + , type: InputType.TEXT + , validation: [ + { type: ValidationType.REGEX, arguments: ["\\d{5}-\\d{3}"] } + , { type: ValidationType.MANDATORY, arguments: [] } + ] + , sugestions: [] + , id: 2 +}; +/** Input Options object to be used on a formOptions */ +const inputOptsDBH3: InputOptions = { + placement: 2 + , description: "Description Question 2 Form 1" + , question: "Question 2 Form 1" + , type: InputType.TEXT + , validation: [ + { type: ValidationType.MAXCHAR, arguments: ["10"] } + , { type: ValidationType.MINCHAR, arguments: ["2"] } + ] + , sugestions: [] + , id: 3 +}; +/** Input Options object to be used on a formOptions */ +const inputOptsDBH4: InputOptions = { + placement: 0 + , description: "Description Question 1 Form 1" + , question: "Question 1 Form 1" + , enabled: true + , type: InputType.TEXT + , validation: [ + { type: ValidationType.MANDATORY, arguments: [] } + ] +}; +/** Input Options object to be used on a formOptions */ +const inputOptsDBH5: InputOptions = { + placement: 1 + , description: "Description Question 2 Form 2" + , question: "Question 2 Form 2" + , enabled: true + , type: InputType.TEXT + , validation: [ + { type: ValidationType.MINCHAR, arguments: ["5"] } + ] +}; +/** InputOptions object to be used as a base to updates */ +const inputObjToUpdateDBH1: InputOptions = { + placement: 0 + , description: "Description Question 1 Form 1" + , question: "Question 1 Form 1" + , type: InputType.TEXT + , validation: [] + , id: 1 +}; +/** InputUpdateOptions object to do an remotion update */ +const updateObjDBH1REMOVE: InputUpdateOptions = { + id: 1 + , input: inputObjToUpdateDBH1 + , inputOperation: UpdateType.REMOVE + , value: null +}; +/** InputUpdateOptions object to do an reenable update */ +const updateObjDBH1REENABLE: InputUpdateOptions = { + id: 1 + , input: inputObjToUpdateDBH1 + , inputOperation: UpdateType.REENABLED + , value: null +}; +/** InputUpdateOptions object to do an remotion update */ +const updateObjDBH2REMOVE: InputUpdateOptions = { + id: 2 + , input: optsInput2 + , inputOperation: UpdateType.REMOVE + , value: null +}; +/** InputUpdateOptions object to do an reenable update */ +const updateObjDBH2REENABLE: InputUpdateOptions = { + id: 2 + , input: optsInput2 + , inputOperation: UpdateType.REENABLED + , value: null +}; +/** InputOptions object to be used as a base to updates */ +const inputObjToUpdateDBH3: InputOptions = { + placement: 2 + , description: "Description Question 3 Form 1" + , question: "Question 3 Form 1" + , type: InputType.TEXT + , validation: [ + { type: ValidationType.REGEX, arguments: ["\\d{5}-\\d{3}"] } + , { type: ValidationType.MANDATORY, arguments: [] } + ] + , id: 2 +}; +/** InputUpdateOptions object to do an remotion update */ +const updateObjDBH3REMOVE: InputUpdateOptions = { + id: 3 + , input: inputObjToUpdateDBH3 + , inputOperation: UpdateType.REMOVE + , value: null +}; +/** InputUpdateOptions object to do an reenable update */ +const updateObjDBH3REENABLE: InputUpdateOptions = { + id: 3 + , input: inputObjToUpdateDBH3 + , inputOperation: UpdateType.REENABLED + , value: null +}; +/** FormOptions object to be used on formupdateOptions */ +const formObjToUpdateDBH: FormOptions = { + id: 1 + , title: "Form Title 1" + , description: "Form Description 1" + , inputs: [ + inputObjToUpdateDBH1 + , optsInput2 + , inputObjToUpdateDBH3 + ] +}; +/** FormUpdateOptions to Upadte the form and insert a formupdate */ +const formUpdateOptsObjDBH1: FormUpdateOptions = { + id: 1 + , form: formObjToUpdateDBH + , updateDate: new Date() + , changed: true + , inputUpdates: [ + updateObjDBH1REMOVE + , updateObjDBH2REMOVE + , updateObjDBH3REMOVE + ] +}; +/** FormUpdateOptions to Upadte the form with a remotion and insert a formupdate */ +const formUpdateOptsObjDBH2: FormUpdateOptions = { + id: 1 + , form: formObjToUpdateDBH + , updateDate: new Date() + , changed: false + , inputUpdates: [ + updateObjDBH1REMOVE + , updateObjDBH2REMOVE + , updateObjDBH3REMOVE + ] +}; +/** FormUpdateOptions to Upadte the form with a reenable operation and insert a formupdate */ +const formUpdateOptsObjDBH3: FormUpdateOptions = { + id: 1 + , form: formObjToUpdateDBH + , updateDate: new Date() + , changed: false + , inputUpdates: [ + updateObjDBH1REENABLE + , updateObjDBH2REENABLE + , updateObjDBH3REENABLE + ] +}; +/** Form options that will have to be read */ +const formOptsObjDBH: FormOptions = { + id: 1 + , title: "Form Title 1" + , description: "Form Description 1" + , inputs: [ + OptHandler.input(inputOptsDBH1) + , OptHandler.input(inputOptsDBH2) + , OptHandler.input(inputOptsDBH3) + ] +}; +/** Form options that will have to be write */ +const formOptsObjDBH2: FormOptions = { + title: "Form Title 1" + , description: "Form Description 1" + , inputs: [ + OptHandler.input(inputOptsDBH4) + , OptHandler.input(inputOptsDBH5) + ] +}; +/** InputAnswerOptions to be used on the first dictionary */ +const inputAnswersOptDBH1: InputAnswerOptions = { + id: 5 + , idInput: 1 + , placement: 0 + , value: "Answer to Question 1 Form 1" +}; +/** InputAnswerOptions to be used on the first dictionary */ +const inputAnswersOptDBH2: InputAnswerOptions = { + id: 6 + , idInput: 2 + , placement: 0 + , value: "Answer to Question 2 Form 1" +}; +/** InputAnswerOptions to be used on the first dictionary */ +const inputAnswersOptDBH3: InputAnswerOptions = { + id: 7 + , idInput: 3 + , placement: 0 + , value: "Answer to Question 3 Form 1" +}; +/** InputAnswerOptions to be used on the second dictionary */ +const inputAnswersOptDBH4: InputAnswerOptions = { + id: undefined + , idInput: 18 + , placement: 0 + , value: "true" +}; +/** InputAnswerOptions to be used on the second dictionary */ +const inputAnswersOptDBH5: InputAnswerOptions = { + id: undefined + , idInput: 18 + , placement: 1 + , value: "true" +}; +/** InputAnswerOptions to be used on the second dictionary */ +const inputAnswersOptDBH6: InputAnswerOptions = { + id: undefined + , idInput: 18 + , placement: 2 + , value: "false" +}; +/** InputAnswerOptions to be used on the second dictionary */ +const inputAnswersOptDBH7: InputAnswerOptions = { + id: undefined + , idInput: 19 + , placement: 1 + , value: "true" +}; +/** InputAnswerOptions to be used on the second dictionary */ +const inputAnswersOptDBH8: InputAnswerOptions = { + id: undefined + , idInput: 20 + , placement: 1 + , value: "true" +}; +/** InputAnswers to be read and written */ +const inputAnswerOptionsDictDBH1: InputAnswerOptionsDict = { + 1: [inputAnswersOptDBH1] + , 2: [inputAnswersOptDBH2] + , 3: [inputAnswersOptDBH3] +}; +/** InputAnswers to be inserted, they have checkbox validation */ +const inputAnswerOptionsDictdbh2: InputAnswerOptionsDict = { + 18: [ + inputAnswersOptDBH4 + , inputAnswersOptDBH5 + , inputAnswersOptDBH6 + ] + , 19: [inputAnswersOptDBH7] + , 20: [inputAnswersOptDBH8] + , 21: [] +}; +/** Input used on a form that will be updated */ +const inputDBH1: Input = { + placement: 0 + , description: "Description Question 1 Form 1" + , question: "Question 1 Form 1" + , enabled: true + , type: InputType.TEXT + , validation: [] + , id: 1 + , sugestions: [] + , subForm: null +}; +/** Input with sugestions used on a form */ +const inputDBH2: Input = { + placement: 0 + , description: "Description Question 1 Form 6" + , question: "Question 1 Form 6" + , enabled: true + , type: InputType.CHECKBOX + , sugestions: [ + { value: "Sugestion 1", placement: 0 } + , { value: "Sugestion 2", placement: 1 } + , { value: "Sugestion 3", placement: 2 } + ] + , validation: [ + { type: ValidationType.SOMECHECKBOX, arguments: [] } + ] + , id: 18 + , subForm: null +}; +/** Input with sugestions used on a form */ +const inputDBH3: Input = { + placement: 1 + , description: "Description Question 2 Form 6" + , question: "Question 2 Form 6" + , enabled: true + , type: InputType.RADIO + , sugestions: [ + { value: "Sugestion 4", placement: 0 } + , { value: "Sugestion 5", placement: 1 } + , { value: "Sugestion 6", placement: 2 } + ] + , subForm: null + , validation: [] + , id: 19 +}; +/** Input with sugestions used on a form */ +const inputDBH4: Input = { + placement: 2 + , description: "Description Question 3 Form 6" + , question: "Question 3 Form 6" + , enabled: true + , type: InputType.SELECT + , sugestions: [ + { value: "Sugestion 1", placement: 0 } + , { value: "Sugestion 2", placement: 1 } + ] + , subForm: null + , validation: [ + { type: ValidationType.DEPENDENCY, arguments: ["19", "1"] } + , { type: ValidationType.DEPENDENCY, arguments: ["20", "1"] } + ] + , id: 20 +}; +/** Input with sugestions used on a form */ +const inputDBH5: Input = { + placement: 3 + , description: "Description Question 4 Form 6" + , question: "Question 4 Form 6" + , enabled: true + , type: InputType.CHECKBOX + , subForm: null + , sugestions: [ + { value: "Sugestion n", placement: 0 } + , { value: "Sugestion n+1", placement: 1 } + , { value: "Sugestion n+2", placement: 2 } + , { value: "Sugestion n+3", placement: 4 } + ] + , validation: [ + { type: ValidationType.SOMECHECKBOX, arguments: [] } + ] + , id: 21 +}; +/** Input used on a form */ +const inputDBH6: Input = { + placement: 4 + , subForm: null + , description: "Description Question 5 Form 6" + , question: "Question 5 Form 6" + , enabled: true + , type: InputType.TEXT + , sugestions: [] + , validation: [ + { type: ValidationType.DEPENDENCY, arguments: ["18", "2"] } + ] + , id: 22 +}; +/** Input with 'typeof' validations used on a form */ +const inputDBH7: Input = { + placement: 0 + , subForm: null + , description: "Description Question 1 Form 7" + , question: "Question 1 form 7" + , enabled: true + , type: InputType.TEXT + , sugestions: [] + , validation: [ + { type: ValidationType.TYPEOF, arguments: ["int"] } + , { type: ValidationType.MAXANSWERS, arguments: ["2"] } + ] + , id: 23 +}; +/** Input with 'typeof' validations used on a form */ +const inputDBH8: Input = { + placement: 1 + , subForm: null + , description: "Description Question 2 Form 7" + , question: "Question 2 form 7" + , enabled: true + , type: InputType.TEXT + , sugestions: [] + , validation: [ + { type: ValidationType.TYPEOF, arguments: ["int"] } + , { type: ValidationType.MAXANSWERS, arguments: ["1"] } + ] + , id: 24 +}; +/** Input with 'typeof' validations used on a form */ +const inputDBH9: Input = { + placement: 2 + , subForm: null + , description: "Description Question 3 Form 7" + , question: "Question 3 form 7" + , enabled: true + , type: InputType.TEXT + , sugestions: [] + , validation: [ + { type: ValidationType.TYPEOF, arguments: ["float"] } + ] + , id: 25 +}; +/** Input with 'typeof' validations used on a form */ +const inputDBH10: Input = { + placement: 3 + , subForm: null + , description: "Description Question 4 Form 7" + , question: "Question 4 form 7" + , enabled: true + , type: InputType.TEXT + , sugestions: [] + , validation: [ + { type: ValidationType.TYPEOF, arguments: ["float"] } + ] + , id: 26 +}; +/** Input with 'typeof' validations used on a form */ +const inputDBH11: Input = { + placement: 4 + , subForm: null + , description: "Description Question 1 Form 8" + , question: "Question 1 form 8" + , enabled: true + , type: InputType.TEXT + , sugestions: [] + , validation: [ + { type: ValidationType.TYPEOF, arguments: ["date"] } + , { type: ValidationType.MAXANSWERS, arguments: ["2"] } + ] + , id: 27 +}; +/** Input with 'typeof' validations used on a form */ +const inputDBH12: Input = { + placement: 5 + , subForm: null + , description: "Description Question 2 Form 8" + , question: "Question 2 form 8" + , enabled: true + , type: InputType.TEXT + , sugestions: [] + , validation: [ + { type: ValidationType.TYPEOF, arguments: ["date"] } + ] + , id: 28 +}; +/** Input with 'typeof' validations used on a form */ +const inputDBH13: Input = { + placement: 6 + , subForm: null + , description: "Description Question 3 Form 8" + , question: "Question 3 form 8" + , enabled: true + , type: InputType.TEXT + , sugestions: [] + , validation: [ + { type: ValidationType.TYPEOF, arguments: ["invalid"] } + , { type: ValidationType.MAXANSWERS, arguments: ["invalid"] } + , { type: ValidationType.DEPENDENCY, arguments: ["28", "invalid"] } + ] + , id: 29 +}; +/** Form used on a formUpdate */ +const formObjDBH1: Form = { + id: 1 + , title: "Form Title 1" + , description: "Form Description 1" + , inputs: [ inputDBH1 ] +}; +/** Form, with sugestions on the inputs, that will be inserted */ +const formObjDBH2: Form = { + title: "Form Title 6" + , description: "Form Description 6" + , inputs: [ + inputDBH2 + , inputDBH3 + , inputDBH4 + , inputDBH5 + , inputDBH6 + ] + , id: 6 +}; +/** Form with inputs having 'typeof' on the validation property */ +const formObjdbh3: Form = { + title: "Form Title 7" + , description: "Form Description 7" + , inputs: [ + inputDBH7 + , inputDBH8 + , inputDBH9 + , inputDBH10 + , inputDBH11 + , inputDBH12 + , inputDBH13 + ] + , id: 7 +}; +/** InputUpdate uesd on a invalid formUpdate */ +const inputUpdateObjDBH: InputUpdate = { + input: inputDBH1 + , inputOperation: 10 + , value: "Invalid Operation" +}; +/** Invalid formUpdate, operation not recognized */ +const formUpdateObjDBH1: FormUpdate = { + form: formObjDBH1 + , updateDate: new Date() + , inputUpdates: [ inputUpdateObjDBH ] +}; +/** User to be inserted */ +const defaultUser: User = new User ({ + id: 2 + , name: "User 2" + , email: "test2@test.com" + , hash: "hashTest2" + , enabled: true +}); +/** User to be inserted with false enable */ +const falseEnabledUser: User = new User ({ + id: 3 + , name: "User 3" + , email: "test3@test.com" + , hash: "hashTest3" + , enabled: false +}); +/** User to be inserted with null enable */ +const nullEnabledUser: User = new User ({ + id: 4 + , name: "User 4" + , email: "test4@test.com" + , hash: "hashTest4" +}); +/** User to be inserted null ID */ +const nullIdUser: User = new User ({ + name: "User 5" + , email: "test5@test.com" + , hash: "hashTest5" + , enabled: true +}); +/** User to be updated */ +const userToUpdate: User = new User ({ + id: 2 + , name: "User updated" + , email: "testUpdate@test.com" + , hash: "hashTest2" + , enabled: true +}); +/** User to update another user's enabled (?) */ +const userToUpdate2: User = new User ({ + id: 3 + , name: "User 3" + , email: "test3@test.com" + , hash: "hashTest3" + , enabled: true +}); + +/** Input used on a form that will be updated */ +const input1Form6: InputOptions = { + placement: 0 + , description: "Description Question 1 Form 1" + , question: "Question 1 Form 1" + , enabled: true + , type: InputType.TEXT + , validation: [] + , id: 1 + , sugestions: [] + , subForm: null +}; +/** Input with sugestions used on a form */ +const input2Form6: InputOptions = { + placement: 0 + , description: "Description Question 1 Form 6" + , question: "Question 1 Form 6" + , enabled: true + , type: InputType.CHECKBOX + , sugestions: [ + { value: "Sugestion 1", placement: 0 } + , { value: "Sugestion 2", placement: 1 } + , { value: "Sugestion 3", placement: 2 } + ] + , validation: [ + { type: ValidationType.SOMECHECKBOX, arguments: [] } + ] + , id: 18 + , subForm: null +}; +/** Input with sugestions used on a form */ +const input3Form6: InputOptions = { + placement: 1 + , description: "Description Question 2 Form 6" + , question: "Question 2 Form 6" + , enabled: true + , type: InputType.RADIO + , sugestions: [ + { value: "Sugestion 4", placement: 0 } + , { value: "Sugestion 5", placement: 1 } + , { value: "Sugestion 6", placement: 2 } + ] + , subForm: null + , validation: [] + , id: 19 +}; +/** Input with sugestions used on a form */ +const input4Form6: InputOptions = { + placement: 2 + , description: "Description Question 3 Form 6" + , question: "Question 3 Form 6" + , enabled: true + , type: InputType.SELECT + , sugestions: [ + { value: "Sugestion 1", placement: 0 } + , { value: "Sugestion 2", placement: 1 } + ] + , subForm: null + , validation: [ + { type: ValidationType.DEPENDENCY, arguments: ["19", "1"] } + , { type: ValidationType.DEPENDENCY, arguments: ["20", "1"] } + ] + , id: 20 +}; +/** Input with sugestions used on a form */ +const input5Form6: InputOptions = { + placement: 3 + , description: "Description Question 4 Form 6" + , question: "Question 4 Form 6" + , enabled: true + , type: InputType.CHECKBOX + , subForm: null + , sugestions: [ + { value: "Sugestion n", placement: 0 } + , { value: "Sugestion n+1", placement: 1 } + , { value: "Sugestion n+2", placement: 2 } + , { value: "Sugestion n+3", placement: 4 } + ] + , validation: [ + { type: ValidationType.SOMECHECKBOX, arguments: [] } + ] + , id: 21 +}; +/** Input used on a form */ +const input6Form6: InputOptions = { + placement: 4 + , subForm: null + , description: "Description Question 5 Form 6" + , question: "Question 5 Form 6" + , enabled: true + , type: InputType.TEXT + , sugestions: [] + , validation: [ + { type: ValidationType.DEPENDENCY, arguments: ["18", "2"] } + ] + , id: 22 +}; +/** Form, with sugestions on the inputs, that will be inserted */ +const formWithId6: FormOptions = { + title: "Form Title 6" + , description: "Form Description 6" + , inputs: [ + input2Form6 + , input3Form6 + , input4Form6 + , input5Form6 + , input6Form6 + ] + , id: 6 +}; +/** ============================================= */ +/** form testing Scenario */ + +/** InputOptions used on a FormOptions */ +const optsInput2EnableTrue: InputOptions = { // optsInput2 but with enabled true + placement: 1 + , description: "Description Question 2 Form 1" + , question: "Question 2 Form 1" + , enabled: true + , type: InputType.TEXT + , validation: [ + { type: ValidationType.MAXCHAR, arguments: ["10"] } + , { type: ValidationType.MINCHAR, arguments: ["2"] } + ] + , id: 3 +}; +/** InputOptions used on a FormOptions */ +const optsInput3EnableTrue: InputOptions = { // Input 3 + placement: 2 + , description: "Description Question 3 Form 1" + , question: "Question 3 Form 1" + , enabled: true + , type: InputType.TEXT + , validation: [ + { type: ValidationType.REGEX, arguments: ["\\d{5}-\\d{3}"] } + , { type: ValidationType.MANDATORY, arguments: [] } + ] + , id: 2 +}; +/** FormOptions to test a receive of a valid form */ +const formObjInput1to3WithTrueEnable: FormOptions = { + id: 1 + , title: "Form Title 1" + , description: "Form Description 1" + , inputs: [ + inputOptsFull + , optsInput2EnableTrue + , optsInput3EnableTrue + ] +}; +/** Valid form to post */ +const validFormOptsToPost: FormOptions = { + title: "Form Title 4" + , description: "Form Description 4" + , inputs: [ + { + placement: 0 + , description: "Description Question 1 Form 4" + , question: "Question 1 Form 4" + , enabled: true + , type: InputType.TEXT + , validation: [] + } + , { + placement: 1 + , description: "Description Question 2 Form 4" + , question: "Question 2 Form 4" + , enabled: true + , type: InputType.TEXT + , validation: [ + { type: 3, arguments: [ "10" ] } + , { type: 4, arguments: [ "2" ] } + ] + } + , { + placement: 2 + , description: "Description Question 3 Form 4" + , question: "Question 3 Form 4" + , enabled: true + , type: InputType.TEXT + , validation: [ + { type: 1, arguments: [ "\\d{5}-\\d{3}" ] } + , { type: 2, arguments: [] } + ] + } + ] +}; +/** Malformed form that misses title */ +const formOptsMissingTitle: any = { + description: "Form Description 4" + , inputs: [ + { + placement: 0 + , description: "Description Question 1 Form 4" + , question: "Question 1 Form 4" + , enabled: true + , type: InputType.TEXT + , validation: [] + } + , { + placement: 1 + , description: "Description Question 2 Form 4" + , question: "Question 2 Form 4" + , enabled: true + , type: InputType.TEXT + , validation: [ + { type: 3, arguments: [ "10" ] } + , { type: 4, arguments: [ "2" ] } + ] + } + , { + placement: 2 + , description: "Description Question 3 Form 4" + , question: "Question 3 Form 4" + , enabled: true + , type: InputType.TEXT + , validation: [ + { type: 1, arguments: [ "\\d{5}-\\d{3}" ] } + , { type: 2, arguments: [] } + ] + } + ] +}; +/** FormOpts to udate with a SWAP operation */ +const formOptsToUpdateSWAP: FormOptions = { + id: 1 + , title: "Form Title 1" + , description: "Form Description 1" + , inputs: [ + { + placement: 0 + , description: "Description Question 1 Form 1" + , question: "Question 1 Form 1" + , enabled: true + , type: InputType.TEXT + , validation: [] + , id: 1 + } + , { + placement: 1 + , description: "Description Question 2 Form 1" + , question: "Question 2 Form 1" + , enabled: true + , type: InputType.TEXT + , validation: [ + { type: 3, arguments: [ "10" ] } + , { type: 4, arguments: [ "2" ] } + ] + , id: 2 + } + , { + placement: 2 + , description: "Description Question 3 Form 1" + , question: "Question 3 Form 1" + , enabled: true + , type: InputType.TEXT + , validation: [ + { type: 3, arguments: [ "10" ] } + , { type: 4, arguments: [ "2" ] } + ] + , id: 3 + } + ] +}; +/** FormOpts to udate with a ADD operation */ +const formOptsToUpdateADD: FormOptions = { + id: 3 + , title: "Form Title 3" + , description: "Form Description 3" + , inputs: [ + { + placement: 0 + , description: "Description Question 1 Form 3" + , question: "Question 1 Form 3" + , enabled: true + , type: InputType.TEXT + , validation: [ + { type: ValidationType.MANDATORY, arguments: [] } + ] + , id: 6 + } + , { + placement: 1 + , description: "Description Question 2 Form 3" + , question: "Question 2 Form 3" + , enabled: true + , type: InputType.TEXT + , validation: [ + { type: 3, arguments: [ "10" ] } + , { type: 4, arguments: [ "2" ] } + ] + , id: 7 + } + , { + placement: 3 + , description: "Description Question 4 Form 3" + , question: "Question 4 Form 3" + , enabled: true + , type: InputType.CHECKBOX + , validation: [] + , sugestions: [ + { value: "Sugestion 1", placement: 0 } + , { value: "Sugestion 2", placement: 1 } + ] + , id: undefined + } + , { + placement: 4 + , description: "Description Question 5 Form 3" + , question: "Question 5 Form 3" + , enabled: true + , type: InputType.TEXT + , validation: [ + { type: 3, arguments: [ "10" ] } + , { type: 4, arguments: [ "2" ] } + ] + , id: undefined + } + , { + placement: 5 + , description: "Description Question 6 Form 3" + , question: "Question 6 Form 3" + , enabled: true + , type: InputType.TEXT + , validation: [ + { type: 3, arguments: [ "10" ] } + , { type: 4, arguments: [ "2" ] } + ] + , id: undefined + } + ] +}; +/** FormOpts to udate with a REMOVE operation */ +const formOptsToUpdateREMOVE: FormOptions = { + id: 3 + , title: "Form Title 3" + , description: "Form Description 3" + , inputs: [] +}; +/** FormOpts to udate with a REENABLE operation */ +const formOptsToUpdateREENABLE: FormOptions = { + id: 3 + , title: "Form Title 3" + , description: "Form Description 3" + , inputs: [ + { + placement: 0 + , description: "Description Question 1 Form 3" + , question: "Question 1 Form 3" + , enabled: true + , type: InputType.TEXT + , validation: [ + { type: ValidationType.MANDATORY, arguments: [] } + ] + , id: 6 + } + , { + placement: 1 + , description: "Description Question 2 Form 1" + , question: "Question 2 Form 1" + , enabled: true + , type: InputType.TEXT + , validation: [ + { type: 3, arguments: [ "10" ] } + , { type: 4, arguments: [ "2" ] } + ] + , id: 7 + } + ] +}; +/** FormOpts to udate changing the title */ +const formOptsToUpdateChangingTheTitle: FormOptions = { + id: 4 + , title: "Title 4" + , description: "Description 4" + , inputs: [ + { + placement: 0 + , description: "Description Question 1 Form 4" + , question: "Question 1 Form 4" + , enabled: true + , type: InputType.TEXT + , validation: [ + { type: ValidationType.MANDATORY, arguments: [] } + ] + , id: 8 + } + , { + placement: 1 + , description: "Description Question 2 Form 4" + , question: "Question 2 Form 4" + , enabled: true + , type: InputType.TEXT + , validation: [ + { type: 3, arguments: [ "10" ] } + , { type: 4, arguments: [ "2" ] } + ] + , id: 9 + } + , { + placement: 2 + , description: "Description Question 3 Form 4" + , question: "Question 3 Form 4" + , enabled: true + , type: InputType.TEXT + , validation: [ + { type: 3, arguments: [ "10" ] } + , { type: 4, arguments: [ "2" ] } + ] + , id: 10 + } + ] +}; +/** FormOpts to udate to Undo changes */ +const formOptsToUpdateUdoingChanges: FormOptions = { + id: 4 + , title: "Form Title 4" + , description: "Form Description 4" + , inputs: [ + { + placement: 0 + , description: "Description Question 1 Form 4" + , question: "Question 1 Form 4" + , enabled: true + , type: InputType.TEXT + , validation: [ + { type: ValidationType.MANDATORY, arguments: [] } + ] + , id: 8 + } + , { + placement: 1 + , description: "Description Question 2 Form 4" + , question: "Question 2 Form 4" + , enabled: true + , type: InputType.TEXT + , validation: [ + { type: 3, arguments: [ "10" ] } + , { type: 4, arguments: [ "2" ] } + ] + , id: 9 + } + , { + placement: 2 + , description: "Description Question 3 Form 4" + , question: "Question 3 Form 4" + , enabled: true + , type: InputType.TEXT + , validation: [ + { type: 3, arguments: [ "10" ] } + , { type: 4, arguments: [ "2" ] } + ] + , id: 10 + } + ] +}; +/** A valid formUpdate */ +const validFormUpdate2: FormOptions = { + id: 1 + , title: "Form Title 1" + , description: "Form Description 1" + , inputs : [ + { + placement: 0 + , description: "Description Question 1 Form 6" + , question: "Question 1 Form 6" + , enabled: true + , type: InputType.TEXT + , validation: [] + , id: 1 + } + , { + placement: 1 + , description: "Description Question 2 Form 6" + , question: "Question 2 Form 6" + , enabled: true + , type: InputType.TEXT + , validation: [ + { type: 3, arguments: [ "10" ] } + , { type: 4, arguments: [ "2" ] } + ] + , id: 2 + } + , { + placement: 2 + , description: "Description Question 3 Form 6" + , question: "Question 3 Form 6" + , enabled: true + , type: InputType.TEXT + , validation: [ + { type: 3, arguments: [ "10" ] } + , { type: 4, arguments: [ "2" ] } + ] + } + ] +}; +/** FormUpdate Options that misses title and description */ +const formOptionsToUpdateMissingProperties: any = { + id: 1 + , inputs : [ + { + placement: 0 + , description: "Description Question 1 Form 1" + , question: "Question 1 Form 1" + , enabled: true + , type: InputType.TEXT + , validation: [] + , id: 1 + } + , { + placement: 1 + , description: "Description Question 2 Form 1" + , question: "Question 2 Form 1" + , enabled: true + , type: InputType.TEXT + , validation: [ + { type: 3, arguments: [ "10" ] } + , { type: 4, arguments: [ "2" ] } + ] + , id: 2 + } + , { + placement: 2 + , description: "Description Question 3 Form 1" + , question: "Question 3 Form 1" + , enabled: true + , type: InputType.TEXT + , validation: [ + { type: 3, arguments: [ "10" ] } + , { type: 4, arguments: [ "2" ] } + ] + } + ] +}; + +const formReadAnswer: FormAnswer[] = + [ { + id: 3, + form: + new Form ({ + id: 1, + title: "Form Title 1", + description: "Form Description 1", + inputs: + [ { + id: 1, + placement: 0, + description: "Description Question 1 Form 1", + question: "Question 1 Form 1", + enabled: true, + type: 0, + sugestions: [], + subForm: null, + validation: [] }, + { + id: 2, + placement: 1, + description: "Description Question 3 Form 1", + question: "Question 3 Form 1", + enabled: true, + type: 0, + sugestions: [], + subForm: null, + validation: + [ { type: 1, arguments: [ "\\d{5}-\\d{3}" ] }, + { type: 2, arguments: [] } ] }, + { + id: 3, + placement: 2, + description: "Description Question 2 Form 1", + question: "Question 2 Form 1", + enabled: true, + type: 0, + sugestions: [], + subForm: null, + validation: + [ { type: 3, arguments: [ "10" ] }, + { type: 4, arguments: [ "2" ] } ] } ] }), + timestamp: new Date("21 february 2019 12:10:25 UTC"), + inputAnswers: + { 1: + [ { + id: 5, + idInput: 1, + placement: 0, + value: "Answer to Question 1 Form 1", + subForm: null } ], + 2: + [ { + id: 6, + idInput: 2, + placement: 0, + value: "Answer to Question 2 Form 1", + subForm: null } ], + 3: + [ { + id: 7, + idInput: 3, + placement: 0, + value: "Answer to Question 3 Form 1", + subForm: null } ] } }, + { + id: 6, + form: + new Form ({ + id: 1, + title: "Form Title 1", + description: "Form Description 1", + inputs: + [ { + id: 1, + placement: 0, + description: "Description Question 1 Form 1", + question: "Question 1 Form 1", + enabled: true, + type: 0, + sugestions: [], + subForm: null, + validation: [] }, + { + id: 2, + placement: 1, + description: "Description Question 3 Form 1", + question: "Question 3 Form 1", + enabled: true, + type: 0, + sugestions: [], + subForm: null, + validation: + [ { type: 1, arguments: [ "\\d{5}-\\d{3}" ] }, + { type: 2, arguments: [] } ] }, + { + id: 3, + placement: 2, + description: "Description Question 2 Form 1", + question: "Question 2 Form 1", + enabled: true, + type: 0, + sugestions: [], + subForm: null, + validation: + [ { type: 3, arguments: [ "10" ] }, + { type: 4, arguments: [ "2" ] } ] } ] }), + timestamp: new Date("22 january 2019 19:10:25"), + inputAnswers: + { 1: + [ { + id: 12, + idInput: 1, + placement: 0, + value: "Answer to Question 1 Form 1", + subForm: null } ], + 2: + [ { + id: 13, + idInput: 2, + placement: 0, + value: "Answer to Question 2 Form 1", + subForm: null } ], + 3: + [ { + id: 14, + idInput: 3, + placement: 0, + value: "Answer to Question 3 Form 1", + subForm: null } ] } }, + { + id: 7, + form: + new Form ({ + id: 1, + title: "Form Title 1", + description: "Form Description 1", + inputs: + [ { + id: 1, + placement: 0, + description: "Description Question 1 Form 1", + question: "Question 1 Form 1", + enabled: true, + type: 0, + sugestions: [], + subForm: null, + validation: [] }, + { + id: 2, + placement: 1, + description: "Description Question 3 Form 1", + question: "Question 3 Form 1", + enabled: true, + type: 0, + sugestions: [], + subForm: null, + validation: + [ { type: 1, arguments: [ "\\d{5}-\\d{3}" ] }, + { type: 2, arguments: [] } ] }, + { + id: 3, + placement: 2, + description: "Description Question 2 Form 1", + question: "Question 2 Form 1", + enabled: true, + type: 0, + sugestions: [], + subForm: null, + validation: + [ { type: 3, arguments: [ "10" ] }, + { type: 4, arguments: [ "2" ] } ] } ] }), + timestamp: new Date("10 february 2020 14:07:49 UTC"), + inputAnswers: + { 1: + [ { + id: 15, + idInput: 1, + placement: 0, + value: "Answer to Question 1 Form 1", + subForm: null } ], + 2: + [ { + id: 16, + idInput: 2, + placement: 0, + value: "12345-000", + subForm: null } ], + 3: + [ { + id: 17, + idInput: 3, + placement: 0, + value: "MAXCHAR 10", + subForm: null } ] } } ]; + +/** A message that is used in cases where the update is a success */ +const successMsg = "Updated"; +/** A message that is used in cases where the update is unsuccess */ +const unsuccessMsg = "Could not update Form. Some error has ocurred. Check error property for details."; + +/** ================================================== */ +/** form testing Scenario */ +/** Valid formAnswer */ +const validFormAnswers: object = { + 1: ["Answer to Question 1 Form 1"] + , 2: ["12345-000"] + , 3: ["MAXCHAR 10"] +}; +/** Invalid formAnswer - wrong RegEx and more than max char limitation */ +const invalidFormAnswer: object = { + 1: ["Answer to Question 1 Form 1"] + , 2: ["12a345-000"] + , 3: ["MAXCHAR 10 AND MORE"] +}; + +/** Answers for forms with subForms */ +/** Date for form answers */ +const date1: Date = new Date("07/08/2007 14:20"); + +/** Answer Form 6 */ +const inputAnswerSubForm5: InputAnswerOptions = { + id: 28 + , idInput: 18 + , placement: 0 + , value: "true" +}; +/** Answer Form 6 */ +const inputAnswerSubForm6: InputAnswerOptions = { + id: 29 + , idInput: 18 + , placement: 1 + , value: "true" +}; +/** Answer Form 6 */ +const inputAnswerSubForm7: InputAnswerOptions = { + id: 30 + , idInput: 18 + , placement: 2 + , value: "false" +}; +/** Answer Form 6 */ +const inputAnswerSubForm8: InputAnswerOptions = { + id: 31 + , idInput: 19 + , placement: 1 + , value: "true" +}; +/** Answer Form 6 */ +const inputAnswerSubForm9: InputAnswerOptions = { + id: 32 + , idInput: 20 + , placement: 1 + , value: "true" +}; +/** Answers dictionary for form 6 */ +const inputAnswerOptDictForm6: InputAnswerOptionsDict = { + 18: [ + inputAnswerSubForm5 + , inputAnswerSubForm6 + , inputAnswerSubForm7 + ] + , 19: [inputAnswerSubForm8] + , 20: [inputAnswerSubForm9] + , 21: [] +}; + +/** SubForm Options for form 8 */ +const subFormAnswerOptForm6: FormAnswerOptions = { + form: new Form(formWithId6) + , timestamp: date1 + , inputsAnswerOptions: inputAnswerOptDictForm6 + , id: 12 +}; +/** Answer Form 8 */ +const inputAnswerSubForm3: InputAnswerOptions = { + id: 33 + , idInput: 33 + , placement: 1 + , value: "Hey you!" +}; +/** Answer Form 8 */ +const inputAnswerSubForm4: InputAnswerOptions = { + id: 27 + , idInput: 32 + , placement: 1 + , value: "" + , subForm: subFormAnswerOptForm6 +}; +/** Answers dictionary for form 8 */ +const inputAnswerOptDictForm8: InputAnswerOptionsDict = { + 32: [inputAnswerSubForm4] + , 33: [inputAnswerSubForm3] +}; + +/** SubForm Options for form 11 */ +const subFormAnswerOptForm8: FormAnswerOptions = { + form: new Form (updatedFormWithValidSubForm2) + , timestamp: date1 + , inputsAnswerOptions: inputAnswerOptDictForm8 + , id: 11 +}; +/** Answer Form 11 */ +const inputAnswerSubForm1: InputAnswerOptions = { + id: 26 + , idInput: 36 + , placement: 0 + , value: "" + , subForm: subFormAnswerOptForm8 +}; +/** Answer Form 11 */ +const inputAnswerSubForm2: InputAnswerOptions = { + id: 34 + , idInput: 37 + , placement: 1 + , value: "Hey!" +}; +/** Answers dictionary for form 11 */ +const inputAnswerOptDictForm11: InputAnswerOptionsDict = { + 36: [inputAnswerSubForm1] + , 37: [inputAnswerSubForm2] +}; + +/** Form Answer for form 11 */ +const formAnswerOptionsForm11: FormAnswerOptions = { + form: new Form(formWithValidSubForm2) + , timestamp: date1 + , inputsAnswerOptions: inputAnswerOptDictForm11 + , id: 10 +}; +/////// form 8 updatedFormWithValidSubForm2 +/////// form 6 formObjDBH2 +/////// anwser formWithValidSubForm2 form 11 + +/** ================================================== */ +/** User testing Scenario */ + +/** Test user to sign up */ +const userTest: User = new User ({ + name: "Test_name" + , email: "test_email@test.com" + , hash: "Test_pw" +}); +/** Test user with the same email as userTest */ +const userTest2: User = new User ({ + name: "Test_name2" + , email: "test_email@test.com" + , hash: "Test_pw" +}); +/** User with null hash property */ +const userNullHash: User = new User ({ + name: "Test_name" + , email: "test_email@test.com" + , hash: null +}); +/** User with null name property */ +const userNullName: any = { + email: "test_email@test.com" + , hash: "Test_pw" +}; +/** User with null email property */ +const userNullEmail: any = { + name: "Test_name" + , hash: "Test_pw" +}; + +/** Sorter test scenario */ +export const sortScenario = { + sortByPlacementRandom : randomPlacement, + orderedPlacement : orderedPlacement +}; +/** ValidationHandler test scenario */ +export const validationHScenario = { + /** Date used on tests */ + date : date, + /** Input Dictionary for testing min char from an input */ + dictMinCharNumber : AnswerDictMinCharNumber, + /** Input Dictionary for testing an mandatory */ + dictMandatoryInput: AnswerDictMandatoryInput, + /** Input Dictionary for testing max char from an input */ + dictMaxCharNumber : AnswerDictMaxChar, + /** Input Dictionary for testing regular expression from an input */ + dictHasARegEx : DictWithRegExInput, + /** Input Dictionary for testing cases where the input isn't: + * Number + * Float + * Date + * Valid answer (including to have more than the form limit answers) + */ + dictCNFD : AnswerOptionsDict, + /** + * Input Dictionary for testing some properties + */ + dictHasSugestion : AnswerDictHasSugestionInput +}; + +/** DiffHandler testing Scenario */ +export const diffHandlerScenario = { + /** New form that should be the result of an REMOVE operation over the base form */ + newFormObjREMOVE : form1, + /** Old form (without any operations) used as a base parameter */ + oldFormObj : formBase, + /** Expected resulting form after REMOVE operation */ + expFormUpdateREMOVE : expFormUpdate1, + /** New form that should be the result of an ADD operation over the base form */ + newFormObjADD : form2, + /** Expected resulting form after ADD operation */ + expFormUpdateADD : expFormUpdate2, + /** New form that should be the result of an SWAP operation over the base form */ + newFormObjSWAP : form3, + /** Expected resulting form after SWAP operation */ + expFormUpdateSWAP : expFormUpdate3, + /** New form that should be the result of a REMOVE and ADD operations over the base form */ + newFormObjREMOVEandADD : form4, + /** Expected resulting form after REMOVE and ADD operations */ + expFormUpdateREMOVEandADD : expFormUpdate4, + /** Old form (without any operations) used as a base parameter when testing all operations */ + oldFormAll : formBase2, + /** New form that should be the result of all operations over the base form */ + newFormObjALL : form5, + /** Expected resulting form after all operations */ + expFormUpdateALL : expFormUpdateALL, + /** New form that should be the result a restoration of an old form */ + newFormObjRESTORE : form6, + /** Old form to be restored */ // REVER + oldFormRestore : formBase3, + /** Expected resulting form after all operations */ + expFormUpdateRESTORE : expFormUpdateRESTORE, + /** New form that should be the result from the creation of a new form */ + newFormObjCREATE : form7, + /** Old empty form to be compared with the newly created */ + FormObjEmpty : emptyForm, + /** Expected resulting form after a creation */ + expFormUpdateCREATE : expFormUpdateCREATION, + /** Expected resulting form after removing all inputs */ + expFormUpdateREMOVEALL : expFormUpdateREMOVEALL, + /** New form with a wrong title */ + newFormObjWrongTitle : form8, + /** Old form with the correct title */ + odlFormObjCorrectTitle : form1, + /** Expected resulting form after updating the title */ + expFormUpdateTITLE : expFormUpdateTITLE, +}; +/** InputUpdate testing scenario */ +export const inputUpdateScenario = { + /** Answer input recieved to update */ + resInputUpdate : resInputUpdate, + /** Expected input after the update */ + expInputUpdate : expInputUpdate +}; +/** input testing scenario */ +export const inputScenario = { + /** Input to be checked its Enable key that should be undefined */ + inputUndefEnable : inputUndefEnableKey, + /** Input to be checked its Enable key that should be null */ + inputNullEnable : inputNullEnabled, + /** Input to be checked its Enable key that should be true */ + inputTrueEnable : inputTrueEnable, + /** Input to be checked its Enable key that should be false */ + inputFalseEnable : inputFalseEnable +}; +/** Enum Handler testing scenario */ +export const enumHandlerScenario = { + + /** Result of stringifying an update, with parameter specifying type 'NONE' */ + sUpdateNone : stringifiedUpdateNone, + /** Result of stringifying an update, with parameter specifying type 'add' */ + sUpdateAdd : stringigiedUpdateAdd, + /** Result of stringifying an update, with parameter specifying type 'remove' */ + sUpdateRemove : stringifiedUpdateRemove, + /** Result of stringifying an update, with parameter specifying type 'swap' */ + sUpdateSwap : stringifiedUpdateSwap, + /** Result of stringifying an update, with parameter specifying type 'reenabled' */ + sUpdateReenabled : stringifiedUpdateReenabled, + + /** Result of parssing an validation, with parameter 'add' */ + pUpdateAdd : parsedUpdateAdd, + /** Result of parssing an validation, with parameter 'ADD' */ + pUpdateAddCapitalLetters : parsedUpdateAddCapitalLetters, + /** Result of parssing an validation, with parameter 'remove' */ + pUpdateRemove : parsedUpdateRemove, + /** Result of parssing an validation, with parameter 'REMOVE' */ + pUpdateRemoveCapitalLetters : parsedUpdateRemoveCapitalLetters, + /** Result of parssing an validation, with parameter 'swap' */ + pUpdateSwap : parsedUpdateSwap, + /** Result of parssing an validation, with parameter 'SWAP' */ + pUpdateSwapCapitalLetters : parsedUpdateSwapCapitalLetters, + /** Result of parssing an validation, with parameter 'reenabled' */ + pUpdateReenabled : parsedUpdateReenabled, + /** Result of parssing an validation, with parameter 'REENABLED' */ + pUpdateReenabledCapitalLetters : parsedUpdateReenabledCapitalLetters, + /** Result of parssing an validation, with parameter '' */ + pUpdateNone : parsedUpdateNone, + /** Result of parssing an validation, with parameter 'fool' */ + pUpdateFOOL : parsedUpdateFOOL, + + /** Result of stringifying an update, with parameter specifying type 'NONE' */ + sInputNone : stringifiedInputNone, + /** Result of stringifying an update, with parameter specifying type 'TEXT' */ + sInputText : stringifiedInputText, + /** Result of stringifying an update, with parameter specifying type 'RADIO' */ + sInputRadio : stringifiedInputRadio, + /** Result of stringifying an update, with parameter specifying type 'CHECKBOX' */ + sInputCheckbox : stringifiedInputCheckbox, + + /** Result of parssing an validation, with parameter '' */ + pInputNone : parsedInputNone, + /** Result of parssing an validation, with parameter 'text' */ + pInputText : parsedInputText, + /** Result of parssing an validation, with parameter 'TEXT' */ + pInputTextCapitalLetters : parsedInputTextCapitalLetters, + /** Result of parssing an validation, with parameter 'radio' */ + pInputRadio : parsedInputRadio, + /** Result of parssing an validation, with parameter 'RADIO' */ + pInputRadioCapitalLetters : parsedInputRadioCapitalLetters, + /** Result of parssing an validation, with parameter 'checkbox' */ + pInputCheckbox : parsedInputCheckbox, + /** Result of parssing an validation, with parameter 'CHECKBOX' */ + pInputCheckboxCapitalLetters : parsedInputCheckboxCapitalLetters, + /** Result of parssing an validation, with parameter 'fool' */ + pInputFOOL : parsedInputFOOL, + /** Result of parssing an validation, with parameter 'select' */ + pInputSelect : parsedInputSelect, + /** Result of parssing an validation, with parameter 'SELECT' */ + pInputSelectCapitalLetters : parsedInputSelectCapitalLetters, + + /** Result of stringifying an update, with parameter specifying type 'REGEX' */ + sValidationRegex : stringifiedValidationRegex, + /** Result of stringifying an update, with parameter specifying type 'MANDATORY' */ + sValidationMandatory : stringifiedValidationMandatory, + /** Result of stringifying an update, with parameter specifying type 'MAXCHAR' */ + sValidationMaxChar : stringifiedValidationMaxChar, + /** Result of stringifying an update, with parameter specifying type 'MINCHAR' */ + sValidationMinChar : stringifiedValidationMinChar, + /** Result of stringifying an update, with parameter specifying type 'TYPEOF' */ + sValidationTypeOf : stringifiedValidationTypeOf, + /** Result of stringifying an update, with parameter specifying type 'SOMECHECKBOX' */ + sValidationSomeCheckbox : stringifiedValidationSomeCheckbox, + /** Result of stringifying an update, with parameter specifying type 'MAXANSWERS' */ + sValidationMaxAnswers : stringifiedValidationMaxAnswers, + /** Result of stringifying an update, with parameter specifying type 'NONE' */ + sValidationNone : stringifiedValidationNone, + + /** Result of parssing an validation, with parameter 'regex' */ + pValidationRegex : parsedValidationRegex, + /** Result of parssing an validation, with parameter 'REGEX' */ + pValidationRegexCapitalized : parsedValidationRegexCapitalized, + /** Result of parssing an validation, with parameter 'mandatory' */ + pValidationMandatory : parsedValidationMandatory, + /** Result of parssing an validation, with parameter 'MANDATORY' */ + pValidationMandatoryCapitalized : parsedValidationMandatoryCapitalized, + /** Result of parssing an validation, with parameter 'maxchar' */ + pValidationMaxChar : parsedValidationMaxChar, + /** Result of parssing an validation, with parameter 'MAXCHAR' */ + pValidationMaxCharyCapitalized : parsedValidationMaxCharyCapitalized, + /** Result of parssing an validation, with parameter 'minchar' */ + pValidationMinChar : parsedValidationMinChar, + /** Result of parssing an validation, with parameter 'MINCHAR' */ + pValidationMinCharyCapitalized : parsedValidationMinCharyCapitalized, + /** Result of parssing an validation, with parameter 'typeof' */ + pValidationTypeOf : parsedValidationTypeOf, + /** Result of parssing an validation, with parameter 'TYPEOF' */ + pValidationTypeOfCapitalized : parsedValidationTypeOfCapitalized, + /** Result of parssing an validation, with parameter 'somecheckbox' */ + pValidationSomeCheckbox : parsedValidationSomeCheckbox, + /** Result of parssing an validation, with parameter 'SOMECHECKBOX' */ + pValidationSomeCheckboxCapitalized : parsedValidationSomeCheckboxCapitalized, + /** Result of parssing an validation, with parameter 'maxanswers' */ + pValidationMaxAnswers : parsedValidationMaxAnswers, + /** Result of parssing an validation, with parameter 'MAXANSWERS' */ + pValidationMaxAnswersCapitalized : parsedValidationMaxAnswersCapitalized, + /** Result of parssing an validation, with parameter 'dependency' */ + pValidationDependency : parsedValidationDependency, + /** Result of parssing an validation, with parameter 'DEPENDENCY' */ + pValidationDependencyCapitalized : parsedValidationDependencyCapitalized, + /** Result of parssing an validation, with parameter '' */ + pValidationNone : parsedValidationNone, + /** Result of parssing an validation, with parameter 'fool' */ + pValidatioFOOL : parsedValidatioFOOL + +}; + +/** formUpdate testing scenario */ +export const formUpdateScenario = { + /** new updated form that should have null id */ + resFormUpdateIdNull: formUpdateNullId, + /** Expected form after the update */ + expFormUpdate: expFormUpdate +}; + +/** optHandler testing scenario */ +export const optHandlerScenario = { + /** Form missing Title property */ + formMissingTitle : formMissingTitle, + /** Form missing Description property */ + formMissingDescription : formMissingDescription, + /** Form missing Inputs properties */ + formMissingInputs : formMissingInputs, + /** Form with Input properties with wrong format */ + formInputNotAnArray : formMissingInputs, + /** Input missing it's Placement property */ + inputMissingPlacement: inputMissingPlacement, + /** Input missing it's Description property */ + inputMissingDescription : inputMissingDescription, + /** Input missing it's Question property */ + inputMissingQuestion : inputMissingQuestion, + /** Input missing it's Type property */ + inputMissingType : inputMissingType, + /** Input missing it's Validation property */ + inputMissingValidation : inputMissingValidation, + /** Input with wrong Validation property format */ + inputValidationNotAnArray : inputValidationNotAnArray, + /** Input with a invalid SubForm. */ + inputWithSubFormWithoutContentFormId : inputWithMalformedSubForm1, + /** Input with a invalid SubForm. */ + inputWithSubFormWithoutInputId : inputWithMalformedSubForm2, + /** FormAnswer with no form associated */ + noFormAssociated : formAnswerHasNoFormAssociated, + /** FormAnswer with no form associated */ + noFormType : formAnswerHasNoFormType, + /** FormAnswer with no Date property */ + formHasNoDate : formHasNoDate, + /** FormAnswer with wrong Date property type */ + formNoDateType : formHasNoDateType, + /** FormAnswer with no inputAnswerOptions dictionary */ + formHasNoDict : formHasNoDict, + /** FormAnswer, InputAnswerOpts missing idInput property */ + missingIdInputOpt : formMissingInputOptIdInput, + /** FormAnswer, InputAnswerOpts missing Placement property */ + missingPlacementInputOpt : formMissingInputOptPlacement, + /** FormAnswer, InputAnswerOpts missing value property */ + missingValueInputOpt : formMissingInputOptValue, + /** FormAnswer containing an valid form */ + validForm : validForm, + /** valid FormUpdate Obj */ + validFormUpdate : validFormUpdateObj, + /** FormUpdate, inputUpdate is not an array */ + nonArrayInputUpdate : formUpdateNotArrayInputUpdates, + /** FormUpdate, missing form property */ + missingFormProperty : formUpdateMissingForm, + /** FormUpdate, missing inputUpdate property */ + missingInputUpdate: formUpdateMissinginputUpdate, + /** InputUpdate missing Input property */ + inputUpdateMissingInput: inputUpdateUndefinedInput, + /** InputUpdate missing inputOperation property */ + missinginputOperation : inputUpdateMissingInputOperation, + /** InputUpdate missing value property */ + missingValueProperty: inputUpdateMissingValue, + /** InputOpt with malformed sugestion that misses placement */ + malformedSugestionMissingPlacement : inputOptsSugestionMissingPlacement, + /** InputOpt with malformed sugestion that misses value */ + malformedSugestionMissingValue : inputOptsSugestionMissingValue, + /** InputOpt type SubForm with a valid subForm */ + validInputWithSubForm : inputWithValidSubForm, + /** InputOpt type SubForm without a SubForm */ + missingSubFormProperty : inputWithoutSubForm +}; + +/** dbHandler testing scenario */ +export const dbHandlerScenario = { + /** Date used in some objects */ + date : dateDBH, + /** Query to insert a form with id 5 on the Database */ + insertForm5 : queryToInsertFormid5, + /** Query to insert a form with id 6 on the Database */ + insertForm6 : queryToInsertFormid6, + /** Query to select all the forms on the Database */ + selectAllForm : queryToSelectAllForms, + /** Query to delete the form with id 6 from the Database */ + deleteForm6 : queryToDeleteFormid6, + /** Query to delete the form with id 5 from the Database */ + deleteForm5 : queryToDeleteFormid5, + /** Query to insert inputs on the Database */ + insertInputs : queryToInsertTwoInputs, + /** Query to select all the inputs on the Database */ + selectAllInputs : queryToSelectAllInputs, + /** Query to try to delete a input that doesn't exist on the Database */ + deleteNonExistentInput : queryToDeleteNonExistentInput, + /** Query to delete the inputs from the Database */ + deleteBothInputs : queryToRemoveBothInputs, + /** Query to insert two input validations on the Database */ + insertInputValidations : queryToInsertTwoInputVal, + /** Query to select all the input validations on the Database */ + selectInputValidations : queryToSelectAllInputVal, + /** Query to try to delete a input validation that doesn't exisit on the Database */ + deleteNonExistetnValidations: queryToRemoveNEInputVal, + /** Query to delete the input validations from the Database */ + deleteInputValidations : queryToRemoveInputVal, + /** Query to insert input validation arguments on the Database */ + insertInputValArguments : queryToInsertInputValArgs, + /** Query to select all the input validation arguments on the Database */ + selectInputValidationArgumetns : queryToSelectAllInputValArgs, + /** Query to try to delete a input validation argument that doesn't exist on the Database */ + deleteNEInputValArgs : queryToRemoveNEInputValArgs, + /** Query to insert input validation arguments from the Database */ + deleteInputValArgs : queryToRemoveInputValArgs, + /** Query to insert form answers on the Database */ + insertFormAnswers : queryToInsertFormAnswers, + /** Query to select all form answers on the Database */ + selectFormAnswers : queryToSelectAllFormAnswers, + /** Query to try to delete a form answer that doesn't exist on the Database */ + deleteNEFormAnswers : queryToRemoveNEFormAnswers, + /** Query to delete the form answers from the Database */ + deleteFormAnswers : queryToRemoveFormAnswers, + /** Query to insert input answers on the Database */ + insertInputAnswers : queryToInsertInputAnswers, + /** Query to select all the input answers on the Database */ + selectInputAnswers : queryToSelectInputAnswers, + /** Query to try to delete a input answer that doesn't exist on the Database */ + removeNEInputAnswers : queryToRemoveNEInputAnswers, + /** Query to remove the input answers from the Database */ + removeInputAnswers : queryToRemoveInputAnswers, + + /** Form options that will have to be read */ + formToRead : formOptsObjDBH, + /** Form options that will have to be write */ + formToWrite : formOptsObjDBH2, + /** Dictionary of inputAnswertOptions that will have to be read */ + inputAnswerToRead : inputAnswerOptionsDictDBH1, + /** Dictionary of inputAnswertOptions that will have to be write */ + inputAnswerToWrite : inputAnswerOptionsDictDBH1, + /** Form update options that will have to update the form and insert a formupdate */ + updateForm : formUpdateOptsObjDBH1, + /** Input update options that will have to update the form and insert a formupdate */ + updateInput : formUpdateOptsObjDBH2, + /** Form update options that will have to reenable a input and insert a formupdate */ + reenabledInputs : formUpdateOptsObjDBH3, + /** Form update that will fail to update by not having it's operation recognized */ + failedUpdate : formUpdateObjDBH1, + /** Form that will have to to be inserted, the inputs have sugestions */ + formWithInputAnswerSugestions : formObjDBH2, + /** Form that will have to to be inserted, the inputs have 'typeof' validations */ + formWithTypeOfValidation : formObjdbh3, + /** Input options that will have to to be inserted, it has 'somecheckbox' validation */ + inputAnswerValidationCheckbox : inputAnswerOptionsDictdbh2, + /** Form with a SubForm */ + formWithSubForm1 : formWithValidSubForm1, + /** Form with a SubForm */ + formWithSubForm2 : formWithValidSubForm2, + /** Form with a SubForm */ + updatedFormWithValidSubForm1 : updatedFormWithValidSubForm1, + /** Form with a SubForm */ + updatedFormWithValidSubForm2 : updatedFormWithValidSubForm2, + /** FormUpdate with a SubForm */ + formUpdateDeleteAddWithSubForm : formUpdateWithSubForm, + /** FormUpdate with a SubForm */ + formUpdateSwapWithSubForm : formUpdateWithSubForm1, + /** Form with a SubForm */ + formWithInvalidSubForm1 : formWithInvalidSubForm1, + /** Form with a SubForm */ + formWithInvalidSubForm2 : formWithInvalidSubForm2, + /** Form with a SubForm */ + formWithInvalidSubForm3 : formWithInvalidSubForm1, + /** FormUpdate with a SubForm */ + formUpdateAddRemoveSubForm : formUpdateWithSubForm1, + /** FormUpdate with a SubForm */ + formUpdateHimselfAsSubForm : formUpdateWithSubForm3, + /** FormUpdate with a SubForm */ + formUpdateWithSubFormLoop : formUpdateWithSubForm4, + /** FormAnswer for Form with SubForm */ + formAnswerWithSubForms : formAnswerOptionsForm11, + + /** User obj to be inserted */ + toBeInserted : defaultUser, + /** User obj to be inserted with false enabled value */ + falseEnabled : falseEnabledUser, + /** User obj to be inserted with null enabled value */ + nullEnabled : nullEnabledUser, + /** User obj to be inserted with null ID value */ + nullId : nullIdUser, + /** User obj to be used to update another user */ + toupdate : userToUpdate, + /** User obj to be used to update another user */ + updateEnable : userToUpdate2, + formTest: form6 +}; + +/** form testing scenario */ +export const formScenario = { + /** Test recieving a valid form */ + validForm : formObjInput1to3WithTrueEnable, + /** Valid form to test posting */ + formToPost : validFormOptsToPost, + /** malformed form to test postng */ + formMissingTitle : formOptsMissingTitle, + /** Update to swap inputs of the form */ + formToSwapInputs : formOptsToUpdateSWAP, + /** Update to add Inputs on the form */ + formToAddInputs : formOptsToUpdateADD, + /** Update to remove inputs from the form */ + formToRemoveInputs : formOptsToUpdateREMOVE, + /** Update to reenable inputs of the form */ + formToReenableInputs : formOptsToUpdateREENABLE, + /** Update to change the form title */ + changeTitle : formOptsToUpdateChangingTheTitle, + /** Update by undoing changes */ + undo : formOptsToUpdateUdoingChanges, + /** A valid formUpdate being used on a non-existent form */ + validUpdate : validFormUpdate2, + /** A malformed try to update a form */ + malformedUpdate : formOptionsToUpdateMissingProperties, + /** Successfuly updating message */ + msg : successMsg, + /** Unsuccesfuly updating message */ + msg2 : unsuccessMsg, + +}; + +/** form testing scenario */ +export const formAnswerScenario = { + /** Valid formAnswer being recieved by a post */ + validAnswer : validFormAnswers, + /** Invalid formAnswer being recieved by a post */ + invalidAnswer : invalidFormAnswer, + /** Form Awnser to be read */ + formAnswerRead : formReadAnswer +}; + +/** User testing scenario */ +export const userScenario = { + /** Correct user to be signed up */ + user : userTest, + /** User with null hash value */ + nullHash : userNullHash, + /** User with null name value */ + nullName : userNullName, + /** User with null email value */ + nullEmail : userNullEmail, + /** User with conflicting email with another already on the db */ + sameEmail : userTest2 + +}; diff --git a/tmp b/tmp new file mode 100644 index 0000000000000000000000000000000000000000..9daeafb9864cf43055ae93beb0afd6c7d144bfa4 --- /dev/null +++ b/tmp @@ -0,0 +1 @@ +test diff --git a/tslint.json b/tslint.json index 35e169ff01d406a42284d0de5fb44f106647ed10..5fad5aa42de36c4573aadd32064f7cd391e9861d 100644 --- a/tslint.json +++ b/tslint.json @@ -5,6 +5,7 @@ "no-var-requires": false, "no-unused-expression": false, "object-literal-sort-keys": false, + "object-literal-shorthand": false, "one-line": false, "trailing-comma": false, "interface-name": false, diff --git a/yarn.lock b/yarn.lock index 769e026da49d12db58011eef969866ed49de96fd..d00b98c0e54076d3837f1d1c4a49db1fbcc6a523 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7,6 +7,11 @@ resolved "https://registry.yarnpkg.com/@types/async/-/async-2.4.1.tgz#43c3b2c60eab41c25ca0009c07ca7d619d943119" integrity sha512-C09BK/wXzbW+/JK9zckhe+FeSbg7NmvVjUWwApnw7ksRpUq3ecGLiq2Aw1LlY4Z/VmtdhSaIs7jO5/MWRYMcOA== +"@types/bcrypt@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/bcrypt/-/bcrypt-3.0.0.tgz#851489a9065a067cb7f3c9cbe4ce9bed8bba0876" + integrity sha512-nohgNyv+1ViVcubKBh0+XiNJ3dO8nYu///9aJ4cgSqv70gBL+94SNy/iC2NLzKPT2Zt/QavrOkBVbZRLZmw6NQ== + "@types/body-parser@*": version "1.17.0" resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.17.0.tgz#9f5c9d9bd04bb54be32d5eb9fc0d8c974e6cf58c" @@ -82,6 +87,13 @@ resolved "https://registry.yarnpkg.com/@types/highlight.js/-/highlight.js-9.12.3.tgz#b672cfaac25cbbc634a0fd92c515f66faa18dbca" integrity sha512-pGF/zvYOACZ/gLGWdQH8zSwteQS1epp68yRcVLJMgUck/MjEn/FBYmPub9pXT8C1e4a8YZfHo1CKyV8q1vKUnQ== +"@types/jsonwebtoken@^8.3.5": + version "8.3.5" + resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-8.3.5.tgz#ff9be1151a844095df1ff5f723651298c2c07659" + integrity sha512-VGM1gb+LwsQ5EPevvbvdnKncajBdYqNcrvixBif1BsiDQiSF1q+j4bBTvKC6Bt9n2kqNSx+yNTY2TVJ360E7EQ== + dependencies: + "@types/node" "*" + "@types/lodash@^4.14.110": version "4.14.122" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.122.tgz#3e31394c38cf1e5949fb54c1192cbc406f152c6c" @@ -186,6 +198,11 @@ ansi-regex@^2.0.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= + ansi-styles@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" @@ -205,6 +222,19 @@ append-transform@^0.4.0: dependencies: default-require-extensions "^1.0.0" +aproba@^1.0.3: + version "1.2.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" + integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== + +are-we-there-yet@~1.1.2: + version "1.1.5" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" + integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w== + dependencies: + delegates "^1.0.0" + readable-stream "^2.0.6" + argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -328,6 +358,14 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= +bcrypt@^3.0.7: + version "3.0.7" + resolved "https://registry.yarnpkg.com/bcrypt/-/bcrypt-3.0.7.tgz#1187d29df2e1cde44268152b13e3d4a655a7c7de" + integrity sha512-K5UglF9VQvBMHl/1elNyyFvAfOY9Bj+rpKrCSR9sFwcW8FywAYJSRwTURNej5TaAK2TEJkcJ6r6lh1YPmspx5Q== + dependencies: + nan "2.14.0" + node-pre-gyp "0.13.0" + body-parser@1.18.3: version "1.18.3" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.18.3.tgz#5b292198ffdd553b3a0f20ded0592b956955c8b4" @@ -357,6 +395,11 @@ browser-stdout@1.3.1: resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== +buffer-equal-constant-time@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" + integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk= + buffer-from@^1.0.0, buffer-from@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" @@ -414,6 +457,16 @@ check-error@^1.0.2: resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" integrity sha1-V00xLt2Iu13YkS6Sht1sCu1KrII= +chownr@^1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.3.tgz#42d837d5239688d55f303003a508230fa6727142" + integrity sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw== + +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= + color-convert@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" @@ -458,6 +511,11 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= +console-control-strings@^1.0.0, console-control-strings@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= + content-disposition@0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" @@ -507,7 +565,7 @@ debug@3.1.0: dependencies: ms "2.0.0" -debug@^3.1.0: +debug@^3.1.0, debug@^3.2.6: version "3.2.6" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== @@ -521,6 +579,11 @@ deep-eql@^3.0.1: dependencies: type-detect "^4.0.0" +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + default-require-extensions@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/default-require-extensions/-/default-require-extensions-1.0.0.tgz#f37ea15d3e13ffd9b437d33e1a75b5fb97874cb8" @@ -533,6 +596,11 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= + depd@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" @@ -550,11 +618,23 @@ detect-indent@^4.0.0: dependencies: repeating "^2.0.0" +detect-libc@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" + integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= + diff@3.5.0, diff@^3.1.0, diff@^3.2.0: version "3.5.0" resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== +ecdsa-sig-formatter@1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" + integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== + dependencies: + safe-buffer "^5.0.1" + ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" @@ -685,11 +765,32 @@ fs-extra@^7.0.0: jsonfile "^4.0.0" universalify "^0.1.0" +fs-minipass@^1.2.5: + version "1.2.7" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" + integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA== + dependencies: + minipass "^2.6.0" + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= +gauge@~2.7.3: + version "2.7.4" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" + integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= + dependencies: + aproba "^1.0.3" + console-control-strings "^1.0.0" + has-unicode "^2.0.0" + object-assign "^4.1.0" + signal-exit "^3.0.0" + string-width "^1.0.1" + strip-ansi "^3.0.1" + wide-align "^1.1.0" + get-func-name@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" @@ -762,6 +863,11 @@ has-flag@^3.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= +has-unicode@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= + he@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" @@ -789,6 +895,20 @@ iconv-lite@0.4.23: dependencies: safer-buffer ">= 2.1.2 < 3" +iconv-lite@^0.4.4: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +ignore-walk@^3.0.1: + version "3.0.3" + resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.3.tgz#017e2447184bfeade7c238e4aefdd1e8f95b1e37" + integrity sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw== + dependencies: + minimatch "^3.0.4" + inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" @@ -802,6 +922,11 @@ inherits@2, inherits@2.0.3, inherits@~2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= +ini@~1.3.0: + version "1.3.5" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" + integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== + interpret@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.2.0.tgz#d5061a6224be58e8083985f5014d844359576296" @@ -826,6 +951,18 @@ is-finite@^1.0.0: dependencies: number-is-nan "^1.0.0" +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + is-utf8@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" @@ -955,6 +1092,74 @@ jsonfile@^4.0.0: optionalDependencies: graceful-fs "^4.1.6" +jsonwebtoken@^8.5.1: + version "8.5.1" + resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d" + integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w== + dependencies: + jws "^3.2.2" + lodash.includes "^4.3.0" + lodash.isboolean "^3.0.3" + lodash.isinteger "^4.0.4" + lodash.isnumber "^3.0.3" + lodash.isplainobject "^4.0.6" + lodash.isstring "^4.0.1" + lodash.once "^4.0.0" + ms "^2.1.1" + semver "^5.6.0" + +jwa@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a" + integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA== + dependencies: + buffer-equal-constant-time "1.0.1" + ecdsa-sig-formatter "1.0.11" + safe-buffer "^5.0.1" + +jws@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" + integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== + dependencies: + jwa "^1.4.1" + safe-buffer "^5.0.1" + +lodash.includes@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" + integrity sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8= + +lodash.isboolean@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" + integrity sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY= + +lodash.isinteger@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" + integrity sha1-YZwK89A/iwTDH1iChAt3sRzWg0M= + +lodash.isnumber@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc" + integrity sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w= + +lodash.isplainobject@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" + integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs= + +lodash.isstring@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" + integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE= + +lodash.once@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" + integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= + lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.4: version "4.17.11" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" @@ -1036,7 +1241,22 @@ minimist@~0.0.1: resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" integrity sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8= -mkdirp@0.5.1, mkdirp@0.5.x, mkdirp@^0.5.1: +minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" + integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== + dependencies: + safe-buffer "^5.1.2" + yallist "^3.0.0" + +minizlib@^1.2.1: + version "1.3.3" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" + integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q== + dependencies: + minipass "^2.9.0" + +mkdirp@0.5.1, mkdirp@0.5.x, mkdirp@^0.5.0, mkdirp@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= @@ -1075,11 +1295,41 @@ ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== +nan@2.14.0: + version "2.14.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" + integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== + +needle@^2.2.1: + version "2.4.0" + resolved "https://registry.yarnpkg.com/needle/-/needle-2.4.0.tgz#6833e74975c444642590e15a750288c5f939b57c" + integrity sha512-4Hnwzr3mi5L97hMYeNl8wRW/Onhy4nUKR/lVemJ8gJedxxUyBLm9kkrDColJvoSfwi0jCNhD+xCdOtiGDQiRZg== + dependencies: + debug "^3.2.6" + iconv-lite "^0.4.4" + sax "^1.2.4" + negotiator@0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" integrity sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk= +node-pre-gyp@0.13.0: + version "0.13.0" + resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.13.0.tgz#df9ab7b68dd6498137717838e4f92a33fc9daa42" + integrity sha512-Md1D3xnEne8b/HGVQkZZwV27WUi1ZRuZBij24TNaZwUPU3ZAFtvT6xxJGaUVillfmMKnn5oD1HoGsp2Ftik7SQ== + dependencies: + detect-libc "^1.0.2" + mkdirp "^0.5.1" + needle "^2.2.1" + nopt "^4.0.1" + npm-packlist "^1.1.6" + npmlog "^4.0.2" + rc "^1.2.7" + rimraf "^2.6.1" + semver "^5.3.0" + tar "^4" + nopt@3.x: version "3.0.6" resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" @@ -1087,11 +1337,54 @@ nopt@3.x: dependencies: abbrev "1" +nopt@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" + integrity sha1-0NRoWv1UFRk8jHUFYC0NF81kR00= + dependencies: + abbrev "1" + osenv "^0.1.4" + +npm-bundled@^1.0.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.1.1.tgz#1edd570865a94cdb1bc8220775e29466c9fb234b" + integrity sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA== + dependencies: + npm-normalize-package-bin "^1.0.1" + +npm-normalize-package-bin@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz#6e79a41f23fd235c0623218228da7d9c23b8f6e2" + integrity sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA== + +npm-packlist@^1.1.6: + version "1.4.7" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.7.tgz#9e954365a06b80b18111ea900945af4f88ed4848" + integrity sha512-vAj7dIkp5NhieaGZxBJB8fF4R0078rqsmhJcAfXZ6O7JJhjhPK96n5Ry1oZcfLXgfun0GWTZPOxaEyqv8GBykQ== + dependencies: + ignore-walk "^3.0.1" + npm-bundled "^1.0.1" + +npmlog@^4.0.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" + integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== + dependencies: + are-we-there-yet "~1.1.2" + console-control-strings "~1.1.0" + gauge "~2.7.3" + set-blocking "~2.0.0" + number-is-nan@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= +object-assign@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + on-finished@~2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" @@ -1114,6 +1407,24 @@ optimist@^0.6.1: minimist "~0.0.1" wordwrap "~0.0.2" +os-homedir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= + +os-tmpdir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= + +osenv@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" + integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.0" + packet-reader@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/packet-reader/-/packet-reader-1.0.0.tgz#9238e5480dedabacfe1fe3f2771063f164157d74" @@ -1255,7 +1566,17 @@ raw-body@2.3.3: iconv-lite "0.4.23" unpipe "1.0.0" -readable-stream@^2.3.5: +rc@^1.2.7: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +readable-stream@^2.0.6, readable-stream@^2.3.5: version "2.3.6" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw== @@ -1306,11 +1627,21 @@ safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== +safe-buffer@^5.0.1, safe-buffer@^5.1.2: + version "5.2.0" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" + integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== + "safer-buffer@>= 2.1.2 < 3": version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== +sax@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== + semver@4.3.2: version "4.3.2" resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.2.tgz#c7a07158a80bedd052355b770d82d6640f803be7" @@ -1321,6 +1652,11 @@ semver@^5.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004" integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg== +semver@^5.6.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + send@0.16.2: version "0.16.2" resolved "https://registry.yarnpkg.com/send/-/send-0.16.2.tgz#6ecca1e0f8c156d141597559848df64730a6bbc1" @@ -1350,6 +1686,11 @@ serve-static@1.13.2: parseurl "~1.3.2" send "0.16.2" +set-blocking@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + setprototypeof@1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" @@ -1364,6 +1705,11 @@ shelljs@^0.8.2: interpret "^1.0.0" rechoir "^0.6.2" +signal-exit@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" + integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= + source-map-support@^0.5.6: version "0.5.10" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.10.tgz#2214080bc9d51832511ee2bab96e3c2f9353120c" @@ -1404,6 +1750,23 @@ statuses@~1.4.0: resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087" integrity sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew== +string-width@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +"string-width@^1.0.2 || 2": + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + string_decoder@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" @@ -1411,13 +1774,20 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -strip-ansi@^3.0.0: +strip-ansi@^3.0.0, strip-ansi@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= dependencies: ansi-regex "^2.0.0" +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= + dependencies: + ansi-regex "^3.0.0" + strip-bom@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" @@ -1425,6 +1795,11 @@ strip-bom@^2.0.0: dependencies: is-utf8 "^0.2.0" +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= + superagent@^3.8.3: version "3.8.3" resolved "https://registry.yarnpkg.com/superagent/-/superagent-3.8.3.tgz#460ea0dbdb7d5b11bc4f78deba565f86a178e128" @@ -1475,6 +1850,19 @@ supports-color@^5.3.0: dependencies: has-flag "^3.0.0" +tar@^4: + version "4.4.13" + resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525" + integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA== + dependencies: + chownr "^1.1.1" + fs-minipass "^1.2.5" + minipass "^2.8.6" + minizlib "^1.2.1" + mkdirp "^0.5.0" + safe-buffer "^5.1.2" + yallist "^3.0.3" + through@2: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" @@ -1626,6 +2014,13 @@ which@^1.1.1: dependencies: isexe "^2.0.0" +wide-align@^1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" + integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== + dependencies: + string-width "^1.0.2 || 2" + wordwrap@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" @@ -1646,6 +2041,11 @@ xtend@^4.0.0: resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" integrity sha1-pcbVMr5lbiPbgg77lDofBJmNY68= +yallist@^3.0.0, yallist@^3.0.3: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + yn@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/yn/-/yn-2.0.0.tgz#e5adabc8acf408f6385fc76495684c88e6af689a"