diff --git a/.eslintrc.js b/.eslintrc.js index 2a2c12554..e56c8fecd 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,82 +1,82 @@ -var path = require('path'); +var path = require("path"); module.exports = { extends: [ - 'airbnb', - 'plugin:jest/recommended', - 'plugin:react-hooks/recommended', - 'plugin:@typescript-eslint/recommended', - 'plugin:cypress/recommended', - ], - parser: '@typescript-eslint/parser', - plugins: [ - 'jest', - 'react', - '@typescript-eslint', + "airbnb", + "plugin:jest/recommended", + "plugin:react-hooks/recommended", + "plugin:@typescript-eslint/recommended", + "plugin:cypress/recommended", + "plugin:prettier/recommended", ], + parser: "@typescript-eslint/parser", + plugins: ["jest", "react", "@typescript-eslint"], env: { node: true, mocha: true, browser: true, - 'jest/globals': true, + "jest/globals": true, }, globals: { expect: false, describe: false, }, rules: { - camelcase: 'off', - 'consistent-return': 1, - 'arrow-body-style': 0, - 'max-len': 0, - 'no-unused-expressions': 0, - 'no-console': 0, - 'space-before-function-paren': 0, - 'react/prefer-stateless-function': 0, - 'react/no-multi-comp': 0, - 'react/no-unused-prop-types': [1, { customValidators: [], skipShapeProps: true }], - 'react/require-default-props': 0, // TODO set default props and enable this check - 'react/jsx-filename-extension': [1, { extensions: ['.jsx', '.tsx'] }], - 'no-param-reassign': 0, - 'new-cap': 0, - 'import/no-unresolved': [2, { caseSensitive: false }], - 'linebreak-style': 0, - 'import/no-named-as-default': 'off', - 'import/no-named-as-default-member': 'off', - 'import/extensions': 0, - 'import/no-extraneous-dependencies': 0, - 'no-underscore-dangle': 0, - 'jsx-a11y/no-static-element-interactions': 'off', + camelcase: "off", + "consistent-return": 1, + "arrow-body-style": 0, + "max-len": 0, + "no-unused-expressions": 0, + "no-console": 0, + "space-before-function-paren": 0, + "react/prefer-stateless-function": 0, + "react/no-multi-comp": 0, + "react/no-unused-prop-types": [ + 1, + { customValidators: [], skipShapeProps: true }, + ], + "react/require-default-props": 0, // TODO set default props and enable this check + "react/jsx-filename-extension": [1, { extensions: [".jsx", ".tsx"] }], + "no-param-reassign": 0, + "new-cap": 0, + "import/no-unresolved": [2, { caseSensitive: false }], + "linebreak-style": 0, + "import/no-named-as-default": "off", + "import/no-named-as-default-member": "off", + "import/extensions": 0, + "import/no-extraneous-dependencies": 0, + "no-underscore-dangle": 0, + "jsx-a11y/no-static-element-interactions": "off", // note you must disable the base rule as it can report incorrect errors. more info here: // https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-use-before-define.md - 'no-use-before-define': 'off', - '@typescript-eslint/no-use-before-define': ['error'], + "no-use-before-define": "off", + "@typescript-eslint/no-use-before-define": ["error"], // turn off and override to not run this on js and jsx files. More info here: // https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/explicit-module-boundary-types.md#configuring-in-a-mixed-jsts-codebase - '@typescript-eslint/explicit-module-boundary-types': 'off', + "@typescript-eslint/explicit-module-boundary-types": "off", // There is a bug with these rules in our version of jsx-a11y plugin (5.1.1) // To upgrade our version of the plugin we would need to make more changes // with eslint-config-airbnb, so we will just turn off for now. - 'jsx-a11y/heading-has-content': 'off', - 'jsx-a11y/anchor-has-content': 'off', + "jsx-a11y/heading-has-content": "off", + "jsx-a11y/anchor-has-content": "off", }, overrides: [ { - files: ['*.ts', '*.tsx'], + files: ["*.ts", "*.tsx"], rules: { // Set to warn for now at the beginning to make migration easier // but want to change this to error when we can. - '@typescript-eslint/explicit-module-boundary-types': ['warn'], + "@typescript-eslint/explicit-module-boundary-types": ["warn"], }, }, ], settings: { - 'import/resolver': { + "import/resolver": { webpack: { - config: path.join(__dirname, 'webpack.config.js'), + config: path.join(__dirname, "webpack.config.js"), }, }, }, diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 95ddb54a3..8b784ac9c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -25,18 +25,18 @@ jobs: ${{ runner.os }}-modules- # It seems faster not to cache Go dependencies - + - name: Install JS Dependencies run: make deps-js - name: Install Go Dependencies run: make deps-go - # Pre-starting dependencies here means they are ready to go when we need them. + # Pre-starting dependencies here means they are ready to go when we need them. - name: Start Infra Dependencies # Use & to background this run: docker-compose up -d mysql_test redis mailhog saml_idp & - + - name: Build Fleet run: | export PATH=$PATH:~/go/bin @@ -51,7 +51,7 @@ jobs: make e2e-setup yarn cypress run --config video=false - + test-js: strategy: matrix: @@ -106,7 +106,33 @@ jobs: - name: Run JS Linting run: | make lint-js - + + check-prettier: + strategy: + matrix: + os: [ ubuntu-latest ] + runs-on: ${{ matrix.os }} + + steps: + - name: Checkout Code + uses: actions/checkout@v2 + + - name: JS Dependency Cache + uses: actions/cache@v2 + with: + path: | + **/node_modules + ~/.cache/Cypress + key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-modules- + + - name: Install JS Dependencies + run: make deps-js + + - name: Run prettier formatting check + run: | + yarn prettier:check test-go: strategy: @@ -122,15 +148,15 @@ jobs: - name: Checkout Code uses: actions/checkout@v2 - # Pre-starting dependencies here means they are ready to go when we need them. + # Pre-starting dependencies here means they are ready to go when we need them. - name: Start Infra Dependencies # Use & to background this run: docker-compose up -d mysql_test redis & - + # It seems faster not to cache Go dependencies - name: Install Go Dependencies run: make deps-go - + - name: Generate static files run: | export PATH=$PATH:~/go/bin @@ -158,4 +184,3 @@ jobs: - name: Run Go Linting run: | make lint-go - diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 000000000..fb328fef1 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,34 @@ +# markdown +*.md + +# output directories +build +vendor +node_modules + +# generated artifacts +assets/bundle*.* +assets/*@*.svg +assets/*@*.png +assets/*@*.eot +assets/*@*.woff +assets/*@*.woff2 +assets/*@*.ttf +frontend/templates/react.tmpl +bindata.go +server/bindata/generated.go +*.cover +*.test +*.log + +# typescript generated test files +tmp/ + +# editors +.vscode +.idea + +# Cypress e2e testing +cypress/screenshots +cypress/videos +cypress/downloads diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1 @@ +{} diff --git a/.sass-lint.yml b/.sass-lint.yml deleted file mode 100644 index 9e991d882..000000000 --- a/.sass-lint.yml +++ /dev/null @@ -1,120 +0,0 @@ -# Linter ReadMe: https://github.com/sasstools/sass-lint/tree/master/docs/rules -files: - include: 'frontend/**/*.s+(a|c)ss' - ignore: - - 'frontend/styles/global/_fonts.scss' - - 'frontend/styles/global/_icons.scss' -options: - formatter: stylish - merge-default-rules: false -rules: - bem-depth: - - 1 - - max-depth: 1 - border-zero: - - 1 - - convention: '0' - brace-style: - - 1 - - allow-single-line: false - class-name-format: - - 1 - - convention: ^(?!js-).* - convention-explanation: should not be written in the form js-* - clean-import-paths: - - 1 - - filename-extension: true - leading-underscore: false - empty-line-between-blocks: - - 1 - - ignore-single-line-rulesets: false - extends-before-declarations: 1 - extends-before-mixins: 1 - final-newline: - - 1 - - include: true - force-attribute-nesting: 1 - force-element-nesting: 1 - force-pseudo-nesting: 1 - function-name-format: - - 1 - - convention: '^[\-_a-z]+$' - convention-explanation: 'Variables must contain only lowercase letters, hyphens, and underscores' - hex-length: - - 1 - - style: short - hex-notation: - - 1 - - style: lowercase - id-name-format: - - 1 - - convention: hyphenatedbem - indentation: - - 1 - - size: 2 - leading-zero: - - 0 - mixin-name-format: - - 1 - - convention: '^[\-_a-z]+$' - convention-explanation: 'Variables must contain only lowercase letters, hyphens, and underscores' - mixins-before-declarations: - - 1 - - exclude: ['breakpoint', 'media', 'placeholder'] - nesting-depth: - - 1 - - max-depth: 4 - no-color-keywords: 1 - no-color-literals: 0 - no-css-comments: 1 - no-duplicate-properties: 1 - no-empty-rulesets: 1 - no-extends: 0 - no-ids: 1 - no-important: 1 - no-invalid-hex: 1 - no-mergeable-selectors: 1 - no-misspelled-properties: - - 1 - - extra-properties: [] - no-qualifying-elements: - - 1 - - allow-element-with-attribute: true - allow-element-with-class: false - allow-element-with-id: false - no-trailing-zero: 1 - no-transition-all: 1 - no-url-protocols: 1 - placeholder-name-format: - - 1 - - convention: hyphenatedbem - property-sort-order: 0 - quotes: - - 1 - - style: single - shorthand-values: 1 - single-line-per-selector: 1 - space-after-bang: - - 1 - - include: false - space-after-colon: - - 1 - - include: true - space-after-comma: 1 - space-before-bang: - - 1 - - include: true - space-before-brace: - - 1 - - include: true - space-before-colon: 1 - space-between-parens: - - 1 - - include: false - trailing-semicolon: 1 - url-quotes: 1 - variable-name-format: - - 1 - - convention: '^[\-_a-z]+$' - convention-explanation: 'Variables must contain only lowercase letters, hyphens, and underscores' - zero-unit: 1 diff --git a/cypress/integration/app/hosts.spec.ts b/cypress/integration/app/hosts.spec.ts index 6efa1bc00..438efc984 100644 --- a/cypress/integration/app/hosts.spec.ts +++ b/cypress/integration/app/hosts.spec.ts @@ -1,27 +1,27 @@ +import * as path from "path"; -import * as path from 'path'; - -describe('Hosts page', () => { +describe("Hosts page", () => { beforeEach(() => { cy.setup(); cy.login(); }); - it('Add new host', () => { - cy.visit('/'); + it("Add new host", () => { + cy.visit("/"); - cy.contains('button', /add new host/i) - .click(); + cy.contains("button", /add new host/i).click(); - cy.contains('a', /download/i).first() + cy.contains("a", /download/i) + .first() .click(); cy.get('a[href*="showSecret"]').click(); // Assert enroll secret downloaded matches the one displayed - cy.readFile(path.join(Cypress.config('downloadsFolder'), 'secret.txt'), { timeout: 3000 }) - .then((contents) => { - cy.get('input[disabled]').should('have.value', contents); - }); + cy.readFile(path.join(Cypress.config("downloadsFolder"), "secret.txt"), { + timeout: 3000, + }).then((contents) => { + cy.get("input[disabled]").should("have.value", contents); + }); }); }); diff --git a/cypress/integration/app/labelflow.spec.ts b/cypress/integration/app/labelflow.spec.ts index e7681303a..e685afdb9 100644 --- a/cypress/integration/app/labelflow.spec.ts +++ b/cypress/integration/app/labelflow.spec.ts @@ -1,59 +1,59 @@ -describe('Label flow', () => { +describe("Label flow", () => { beforeEach(() => { cy.setup(); cy.login(); }); - it('Create, edit, and delete a label successfully', () => { - cy.visit('/hosts/manage'); + it("Create, edit, and delete a label successfully", () => { + cy.visit("/hosts/manage"); - cy.findByRole('button', { name: /add new label/i }).click(); + cy.findByRole("button", { name: /add new label/i }).click(); // Using class selector because third party element doesn't work with Cypress Testing Selector Library - cy.get('.ace_content') + cy.get(".ace_content") .click() - .type('{selectall}{backspace}SELECT * FROM users;'); + .type("{selectall}{backspace}SELECT * FROM users;"); - cy.findByLabelText(/name/i).click().type('Show all users'); + cy.findByLabelText(/name/i).click().type("Show all users"); cy.findByLabelText(/description/i) .click() - .type('Select all users across platforms.'); + .type("Select all users across platforms."); // Cannot call cy.select on div disguised as a dropdown cy.findByText(/select one/i).click(); cy.findByText(/all platforms/i).click(); - cy.findByRole('button', { name: /save label/i }).click(); + cy.findByRole("button", { name: /save label/i }).click(); cy.findByText(/show all users/i).click(); - cy.contains('button', /edit/i).click(); + cy.contains("button", /edit/i).click(); // Label SQL not editable to test cy.findByLabelText(/name/i) .click() - .type('{selectall}{backspace}Show all usernames'); + .type("{selectall}{backspace}Show all usernames"); cy.findByLabelText(/description/i) .click() - .type('{selectall}{backspace}Select all usernames on Mac.'); + .type("{selectall}{backspace}Select all usernames on Mac."); cy.findByText(/select one/i).click(); cy.findAllByText(/macos/i).click(); - cy.findByRole('button', { name: /update label/i }).click(); + cy.findByRole("button", { name: /update label/i }).click(); - cy.findByRole('button', { name: /delete/i }).click(); + cy.findByRole("button", { name: /delete/i }).click(); // Can't figure out how attach findByRole onto modal button // Can't use findByText because delete button under modal - cy.get('.manage-hosts__modal-buttons > .button--alert') - .contains('button', /delete/i) + cy.get(".manage-hosts__modal-buttons > .button--alert") + .contains("button", /delete/i) .click(); - cy.findByText(/show all users/i).should('not.exist'); + cy.findByText(/show all users/i).should("not.exist"); }); }); diff --git a/cypress/integration/app/packflow.spec.ts b/cypress/integration/app/packflow.spec.ts index 089faf999..f554af062 100644 --- a/cypress/integration/app/packflow.spec.ts +++ b/cypress/integration/app/packflow.spec.ts @@ -1,25 +1,25 @@ -describe('Pack flow', () => { +describe("Pack flow", () => { beforeEach(() => { cy.setup(); cy.login(); }); - it('Create, edit, and delete a pack successfully', () => { - cy.visit('/packs/manage'); + it("Create, edit, and delete a pack successfully", () => { + cy.visit("/packs/manage"); - cy.findByRole('button', { name: /create new pack/i }).click(); + cy.findByRole("button", { name: /create new pack/i }).click(); cy.findByLabelText(/query pack title/i) .click() - .type('Errors and crashes'); + .type("Errors and crashes"); cy.findByLabelText(/query pack description/i) .click() - .type('See all user errors and window crashes.'); + .type("See all user errors and window crashes."); - cy.findByRole('button', { name: /save query pack/i }).click(); + cy.findByRole("button", { name: /save query pack/i }).click(); - cy.visit('/packs/manage'); + cy.visit("/packs/manage"); cy.findByText(/errors and crashes/i).click(); @@ -27,28 +27,28 @@ describe('Pack flow', () => { cy.findByLabelText(/query pack title/i) .click() - .type('{selectall}{backspace}Server errors'); + .type("{selectall}{backspace}Server errors"); cy.findByLabelText(/query pack description/i) .click() - .type('{selectall}{backspace}See all server errors.'); + .type("{selectall}{backspace}See all server errors."); - cy.findByRole('button', { name: /save/i }).click(); + cy.findByRole("button", { name: /save/i }).click(); - cy.visit('/packs/manage'); + cy.visit("/packs/manage"); - cy.get('#select-pack-1').check({ force: true }); + cy.get("#select-pack-1").check({ force: true }); - cy.findByRole('button', { name: /delete/i }).click(); + cy.findByRole("button", { name: /delete/i }).click(); // Can't figure out how attach findByRole onto modal button // Can't use findByText because delete button under modal - cy.get('.all-packs-page__modal-btn-wrap > .button--alert') - .contains('button', /delete/i) + cy.get(".all-packs-page__modal-btn-wrap > .button--alert") + .contains("button", /delete/i) .click(); - cy.findByText(/successfully deleted/i).should('be.visible'); + cy.findByText(/successfully deleted/i).should("be.visible"); - cy.findByText(/server errors/i).should('not.exist'); + cy.findByText(/server errors/i).should("not.exist"); }); }); diff --git a/cypress/integration/app/queryflow.spec.ts b/cypress/integration/app/queryflow.spec.ts index 041611803..2a4664b20 100644 --- a/cypress/integration/app/queryflow.spec.ts +++ b/cypress/integration/app/queryflow.spec.ts @@ -1,63 +1,66 @@ -describe('Query flow', () => { +describe("Query flow", () => { beforeEach(() => { cy.setup(); cy.login(); }); - it('Create, check, edit, and delete a query successfully', () => { - cy.visit('/queries/manage'); + it("Create, check, edit, and delete a query successfully", () => { + cy.visit("/queries/manage"); - cy.findByRole('button', { name: /create new query/i }).click(); + cy.findByRole("button", { name: /create new query/i }).click(); - cy.findByLabelText(/query title/i).click().type('Query all window crashes'); + cy.findByLabelText(/query title/i) + .click() + .type("Query all window crashes"); // Using class selector because third party element doesn't work with Cypress Testing Selector Library - cy.get('.ace_content') + cy.get(".ace_content") .click() - .type('{selectall}{backspace}SELECT * FROM windows_crashes;'); + .type("{selectall}{backspace}SELECT * FROM windows_crashes;"); - cy.findByLabelText(/description/i).click().type('See all window crashes'); + cy.findByLabelText(/description/i) + .click() + .type("See all window crashes"); - cy.findByRole('button', { name: /save/i }).click(); + cy.findByRole("button", { name: /save/i }).click(); - cy.findByRole('button', { name: /save as new/i }).click(); + cy.findByRole("button", { name: /save as new/i }).click(); // Just refreshes to create new query, needs success alert to user that they created a query - cy.visit('/queries/manage'); + cy.visit("/queries/manage"); cy.findByText(/query all/i).click(); - cy.findByRole('button', { name: /edit or run query/i }).click(); + cy.findByRole("button", { name: /edit or run query/i }).click(); - cy.get('.ace_content') + cy.get(".ace_content") .click() .type( - '{selectall}{backspace}SELECT datetime, username FROM windows_crashes;', + "{selectall}{backspace}SELECT datetime, username FROM windows_crashes;" ); - cy.findByRole('button', { name: /save/i }).click(); + cy.findByRole("button", { name: /save/i }).click(); - cy.findByRole('button', { name: /save changes/i }).click(); + cy.findByRole("button", { name: /save changes/i }).click(); - cy.findByText(/query updated/i).should('be.visible'); + cy.findByText(/query updated/i).should("be.visible"); - cy.visit('/queries/manage'); + cy.visit("/queries/manage"); // This element has no label, text, or role - cy.get('#query-checkbox-1') - .check({ force: true }); + cy.get("#query-checkbox-1").check({ force: true }); - cy.findByRole('button', { name: /delete/i }).click(); + cy.findByRole("button", { name: /delete/i }).click(); // Can't figure out how attach findByRole onto modal button // Can't use findByText because delete button under modal - cy.get('.manage-queries-page__modal-btn-wrap > .button--alert') - .contains('button', /delete/i) + cy.get(".manage-queries-page__modal-btn-wrap > .button--alert") + .contains("button", /delete/i) .click(); - cy.findByText(/successfully deleted/i).should('be.visible'); + cy.findByText(/successfully deleted/i).should("be.visible"); - cy.findByText(/query all/i).should('not.exist'); + cy.findByText(/query all/i).should("not.exist"); }); }); diff --git a/cypress/integration/sessions/sessions.spec.ts b/cypress/integration/sessions/sessions.spec.ts index dda6ff86a..fa17ebc57 100644 --- a/cypress/integration/sessions/sessions.spec.ts +++ b/cypress/integration/sessions/sessions.spec.ts @@ -1,51 +1,43 @@ -describe('Sessions', () => { +describe("Sessions", () => { // Typically we want to use a beforeEach but not much happens in these tests // so sharing some state should be okay and saves a bit of runtime. before(() => { cy.setup(); }); - it('Logs in and out successfully', () => { - cy.visit('/'); + it("Logs in and out successfully", () => { + cy.visit("/"); cy.contains(/forgot password/i); // Log in - cy.get('input').first() - .type('test@fleetdm.com'); - cy.get('input').last() - .type('admin123#'); - cy.get('button') - .click(); + cy.get("input").first().type("test@fleetdm.com"); + cy.get("input").last().type("admin123#"); + cy.get("button").click(); // Verify dashboard - cy.url().should('include', '/hosts/manage'); - cy.contains('All Hosts'); + cy.url().should("include", "/hosts/manage"); + cy.contains("All Hosts"); // Log out - cy.findByAltText(/user avatar/i) - .click(); - cy.contains('button', 'Sign out') - .click(); + cy.findByAltText(/user avatar/i).click(); + cy.contains("button", "Sign out").click(); - cy.url().should('match', /\/login$/); + cy.url().should("match", /\/login$/); }); - it('Fails login with invalid password', () => { - cy.visit('/'); - cy.get('input').first() - .type('test@fleetdm.com'); - cy.get('input').last() - .type('bad_password'); - cy.get('.button') - .click(); + it("Fails login with invalid password", () => { + cy.visit("/"); + cy.get("input").first().type("test@fleetdm.com"); + cy.get("input").last().type("bad_password"); + cy.get(".button").click(); - cy.url().should('match', /\/login$/); - cy.contains('Authentication failed'); + cy.url().should("match", /\/login$/); + cy.contains("Authentication failed"); }); - it('Fails to access authenticated resource', () => { - cy.visit('/hosts/manage'); + it("Fails to access authenticated resource", () => { + cy.visit("/hosts/manage"); - cy.url().should('match', /\/login$/); + cy.url().should("match", /\/login$/); }); }); diff --git a/cypress/integration/sessions/sso.spec.ts b/cypress/integration/sessions/sso.spec.ts index 7251375e5..c295d4ecb 100644 --- a/cypress/integration/sessions/sso.spec.ts +++ b/cypress/integration/sessions/sso.spec.ts @@ -1,65 +1,59 @@ -describe('SSO Sessions', () => { +describe("SSO Sessions", () => { beforeEach(() => { cy.setup(); }); - it('Can login with username/password', () => { + it("Can login with username/password", () => { cy.login(); - cy.setupSSO(enable_idp_login = true); + cy.setupSSO((enable_idp_login = true)); cy.logout(); - cy.visit('/'); + cy.visit("/"); cy.contains(/forgot password/i); // Log in - cy.get('input').first() - .type('test@fleetdm.com'); - cy.get('input').last() - .type('admin123#'); - cy.contains('button', 'Login') - .click(); + cy.get("input").first().type("test@fleetdm.com"); + cy.get("input").last().type("admin123#"); + cy.contains("button", "Login").click(); // Verify dashboard - cy.url().should('include', '/hosts/manage'); - cy.contains('All Hosts'); + cy.url().should("include", "/hosts/manage"); + cy.contains("All Hosts"); // Log out - cy.findByAltText(/user avatar/i) - .click(); - cy.contains('button', 'Sign out') - .click(); + cy.findByAltText(/user avatar/i).click(); + cy.contains("button", "Sign out").click(); - cy.url().should('match', /\/login$/); + cy.url().should("match", /\/login$/); }); - it('Can login via SSO', () => { + it("Can login via SSO", () => { cy.login(); - cy.setupSSO(enable_idp_login = true); + cy.setupSSO((enable_idp_login = true)); cy.logout(); - - cy.visit('/'); + cy.visit("/"); // Log in - cy.contains('button', 'Sign On With SimpleSAML'); + cy.contains("button", "Sign On With SimpleSAML"); cy.loginSSO(); - cy.contains('All hosts'); + cy.contains("All hosts"); }); - it('Fails when IdP login disabled', () => { + it("Fails when IdP login disabled", () => { cy.login(); cy.setupSSO(); cy.logout(); - cy.visit('/'); + cy.visit("/"); - cy.contains('button', 'Sign On With SimpleSAML'); + cy.contains("button", "Sign On With SimpleSAML"); cy.loginSSO(); // Log in should fail - cy.contains('Password'); + cy.contains("Password"); }); }); diff --git a/cypress/integration/setup/setup.spec.ts b/cypress/integration/setup/setup.spec.ts index eb660fe98..f7390a65f 100644 --- a/cypress/integration/setup/setup.spec.ts +++ b/cypress/integration/setup/setup.spec.ts @@ -1,46 +1,41 @@ -describe('Setup', () => { +describe("Setup", () => { // Different than normal beforeEach because we don't run the fleetctl setup. beforeEach(() => { - cy.exec('make e2e-reset-db', { timeout: 5000 }); + cy.exec("make e2e-reset-db", { timeout: 5000 }); }); - it('Completes setup', () => { - cy.visit('/'); - cy.url().should('match', /\/setup$/); + it("Completes setup", () => { + cy.visit("/"); + cy.url().should("match", /\/setup$/); cy.contains(/setup/i); // Page 1 - cy.findByPlaceholderText(/username/i) - .type('test'); + cy.findByPlaceholderText(/username/i).type("test"); - cy.findByPlaceholderText(/^password/i).first() - .type('admin123#'); + cy.findByPlaceholderText(/^password/i) + .first() + .type("admin123#"); - cy.findByPlaceholderText(/confirm password/i).last() - .type('admin123#'); + cy.findByPlaceholderText(/confirm password/i) + .last() + .type("admin123#"); - cy.findByPlaceholderText(/email/i) - .type('test@fleetdm.com'); + cy.findByPlaceholderText(/email/i).type("test@fleetdm.com"); - cy.contains('button:enabled', /next/i) - .click(); + cy.contains("button:enabled", /next/i).click(); // Page 2 - cy.findByPlaceholderText(/organization name/i) - .type('Fleet Test'); + cy.findByPlaceholderText(/organization name/i).type("Fleet Test"); - cy.contains('button:enabled', /next/i) - .click(); + cy.contains("button:enabled", /next/i).click(); // Page 3 - cy.contains('button:enabled', /submit/i) - .click(); + cy.contains("button:enabled", /submit/i).click(); // Page 4 - cy.contains('button:enabled', /finish/i) - .click(); + cy.contains("button:enabled", /finish/i).click(); - cy.url().should('match', /\/hosts\/manage$/i); + cy.url().should("match", /\/hosts\/manage$/i); cy.contains(/all hosts/i); }); }); diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts index 7a646c621..800638d61 100644 --- a/cypress/support/commands.ts +++ b/cypress/support/commands.ts @@ -1,4 +1,4 @@ -import '@testing-library/cypress/add-commands'; +import "@testing-library/cypress/add-commands"; // *********************************************** // This example commands.js shows you how to @@ -26,85 +26,89 @@ import '@testing-library/cypress/add-commands'; // -- This will overwrite an existing command -- // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) -Cypress.Commands.add('setup', () => { - cy.exec('make e2e-reset-db e2e-setup', { timeout: 20000 }); +Cypress.Commands.add("setup", () => { + cy.exec("make e2e-reset-db e2e-setup", { timeout: 20000 }); }); -Cypress.Commands.add('login', (username, password) => { - username ||= 'test'; - password ||= 'admin123#'; - cy.request('POST', '/api/v1/fleet/login', { username, password }) - .then((resp) => { - window.localStorage.setItem('KOLIDE::auth_token', resp.body.token); - }); +Cypress.Commands.add("login", (username, password) => { + username ||= "test"; + password ||= "admin123#"; + cy.request("POST", "/api/v1/fleet/login", { username, password }).then( + (resp) => { + window.localStorage.setItem("KOLIDE::auth_token", resp.body.token); + } + ); }); -Cypress.Commands.add('logout', () => { +Cypress.Commands.add("logout", () => { cy.request({ - url: '/api/v1/fleet/logout', - method: 'POST', + url: "/api/v1/fleet/logout", + method: "POST", body: {}, auth: { - bearer: window.localStorage.getItem('KOLIDE::auth_token'), + bearer: window.localStorage.getItem("KOLIDE::auth_token"), }, }); }); -Cypress.Commands.add('setupSSO', (enable_idp_login = false) => { +Cypress.Commands.add("setupSSO", (enable_idp_login = false) => { const body = { sso_settings: { enable_sso: true, enable_sso_idp_login: enable_idp_login, - entity_id: 'https://localhost:8080', - idp_name: 'SimpleSAML', - issuer_uri: 'http://localhost:8080/simplesaml/saml2/idp/SSOService.php', - metadata_url: 'http://localhost:9080/simplesaml/saml2/idp/metadata.php', + entity_id: "https://localhost:8080", + idp_name: "SimpleSAML", + issuer_uri: "http://localhost:8080/simplesaml/saml2/idp/SSOService.php", + metadata_url: "http://localhost:9080/simplesaml/saml2/idp/metadata.php", }, }; cy.request({ - url: '/api/v1/fleet/config', - method: 'PATCH', + url: "/api/v1/fleet/config", + method: "PATCH", body, auth: { - bearer: window.localStorage.getItem('KOLIDE::auth_token'), + bearer: window.localStorage.getItem("KOLIDE::auth_token"), }, }); }); -Cypress.Commands.add('loginSSO', () => { +Cypress.Commands.add("loginSSO", () => { // Note these requests set cookies that are required for the SSO flow to // work properly. This is handled automatically by the browser. cy.request({ - method: 'GET', - url: 'http://localhost:9080/simplesaml/saml2/idp/SSOService.php?spentityid=https://localhost:8080', + method: "GET", + url: + "http://localhost:9080/simplesaml/saml2/idp/SSOService.php?spentityid=https://localhost:8080", followRedirect: false, }).then((firstResponse) => { const redirect = firstResponse.headers.location; cy.request({ - method: 'GET', + method: "GET", url: redirect, followRedirect: false, }).then((secondResponse) => { - const el = document.createElement('html'); + const el = document.createElement("html"); el.innerHTML = secondResponse.body; - const authState = el.getElementsByTagName('input').namedItem('AuthState').defaultValue; + const authState = el.getElementsByTagName("input").namedItem("AuthState") + .defaultValue; cy.request({ - method: 'POST', + method: "POST", url: redirect, body: `username=user1&password=user1pass&AuthState=${authState}`, form: true, followRedirect: false, }).then((finalResponse) => { el.innerHTML = finalResponse.body; - const saml = el.getElementsByTagName('input').namedItem('SAMLResponse').defaultValue; + const saml = el.getElementsByTagName("input").namedItem("SAMLResponse") + .defaultValue; // Load the callback URL with the response from the IdP cy.visit({ - url: '/api/v1/fleet/sso/callback', - method: 'POST', + url: "/api/v1/fleet/sso/callback", + method: "POST", body: { SAMLResponse: saml, }, diff --git a/cypress/support/index.js b/cypress/support/index.js index 37a498fb5..d076cec9f 100644 --- a/cypress/support/index.js +++ b/cypress/support/index.js @@ -14,7 +14,7 @@ // *********************************************************** // Import commands.js using ES2015 syntax: -import './commands'; +import "./commands"; // Alternatively you can use CommonJS syntax: // require('./commands') diff --git a/cypress/tsconfig.json b/cypress/tsconfig.json index 1b532cc84..74b7cb33d 100644 --- a/cypress/tsconfig.json +++ b/cypress/tsconfig.json @@ -4,7 +4,5 @@ "lib": ["es5", "dom"], "types": ["cypress", "@testing-library/cypress", "node"] }, - "include": [ - "**/*.ts" - ] + "include": ["**/*.ts"] } diff --git a/frontend/__mocks__/fileMock.js b/frontend/__mocks__/fileMock.js index 59890f6a2..ea1367c1e 100644 --- a/frontend/__mocks__/fileMock.js +++ b/frontend/__mocks__/fileMock.js @@ -1,3 +1,3 @@ // __mocks__/fileMock.js -module.exports = 'test-file-stub'; +module.exports = "test-file-stub"; diff --git a/frontend/app_constants/APP_SETTINGS.js b/frontend/app_constants/APP_SETTINGS.js index 2cc557cb1..bcf94ab6a 100644 --- a/frontend/app_constants/APP_SETTINGS.js +++ b/frontend/app_constants/APP_SETTINGS.js @@ -1,4 +1,4 @@ export default { - FAKE_PASSWORD: '********', - DEFAULT_SMTP_PORT: '587', + FAKE_PASSWORD: "********", + DEFAULT_SMTP_PORT: "587", }; diff --git a/frontend/app_constants/index.js b/frontend/app_constants/index.js index d789e5275..6bfd5fb5c 100644 --- a/frontend/app_constants/index.js +++ b/frontend/app_constants/index.js @@ -1,6 +1,6 @@ -import APP_SETTINGS from 'app_constants/APP_SETTINGS'; -import HTTP_STATUS from 'app_constants/HTTP_STATUS'; -import PATHS from 'router/paths'; +import APP_SETTINGS from "app_constants/APP_SETTINGS"; +import HTTP_STATUS from "app_constants/HTTP_STATUS"; +import PATHS from "router/paths"; export default { APP_SETTINGS, diff --git a/frontend/components/App/App.jsx b/frontend/components/App/App.jsx index 28bd1173c..07d189dc1 100644 --- a/frontend/components/App/App.jsx +++ b/frontend/components/App/App.jsx @@ -1,13 +1,13 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import { noop } from 'lodash'; -import classnames from 'classnames'; +import React, { Component } from "react"; +import PropTypes from "prop-types"; +import { connect } from "react-redux"; +import { noop } from "lodash"; +import classnames from "classnames"; -import { authToken } from 'utilities/local'; -import { fetchCurrentUser } from 'redux/nodes/auth/actions'; -import { getConfig, getEnrollSecret } from 'redux/nodes/app/actions'; -import userInterface from 'interfaces/user'; +import { authToken } from "utilities/local"; +import { fetchCurrentUser } from "redux/nodes/auth/actions"; +import { getConfig, getEnrollSecret } from "redux/nodes/app/actions"; +import userInterface from "interfaces/user"; export class App extends Component { static propTypes = { @@ -20,47 +20,36 @@ export class App extends Component { dispatch: noop, }; - componentWillMount () { + componentWillMount() { const { dispatch, user } = this.props; if (!user && authToken()) { - dispatch(fetchCurrentUser()) - .catch(() => false); + dispatch(fetchCurrentUser()).catch(() => false); } if (user) { - dispatch(getConfig()) - .catch(() => false); - dispatch(getEnrollSecret()) - .catch(() => false); + dispatch(getConfig()).catch(() => false); + dispatch(getEnrollSecret()).catch(() => false); } return false; } - componentWillReceiveProps (nextProps) { + componentWillReceiveProps(nextProps) { const { dispatch, user } = nextProps; if (user && this.props.user !== user) { - dispatch(getConfig()) - .catch(() => false); - dispatch(getEnrollSecret()) - .catch(() => false); + dispatch(getConfig()).catch(() => false); + dispatch(getEnrollSecret()).catch(() => false); } } - render () { + render() { const { children } = this.props; - const wrapperStyles = classnames( - 'wrapper', - ); + const wrapperStyles = classnames("wrapper"); - return ( -
- {children} -
- ); + return
{children}
; } } diff --git a/frontend/components/App/App.tests.jsx b/frontend/components/App/App.tests.jsx index 509bdc5be..345ffff33 100644 --- a/frontend/components/App/App.tests.jsx +++ b/frontend/components/App/App.tests.jsx @@ -1,74 +1,77 @@ -import { mount } from 'enzyme'; +import { mount } from "enzyme"; -import ConnectedApp from './App'; -import * as authActions from '../../redux/nodes/auth/actions'; -import helpers from '../../test/helpers'; -import local from '../../utilities/local'; +import ConnectedApp from "./App"; +import * as authActions from "../../redux/nodes/auth/actions"; +import helpers from "../../test/helpers"; +import local from "../../utilities/local"; -const { - connectedComponent, - reduxMockStore, -} = helpers; +const { connectedComponent, reduxMockStore } = helpers; -describe('App - component', () => { +describe("App - component", () => { const store = { app: {}, auth: {}, notifications: {} }; const mockStore = reduxMockStore(store); - const component = mount( - connectedComponent(ConnectedApp, { mockStore }), - ); + const component = mount(connectedComponent(ConnectedApp, { mockStore })); afterEach(() => { - local.setItem('auth_token', null); + local.setItem("auth_token", null); }); - it('renders', () => { + it("renders", () => { expect(component).toBeTruthy(); }); - it('loads the current user if there is an auth token but no user', () => { - local.setItem('auth_token', 'ABC123'); + it("loads the current user if there is an auth token but no user", () => { + local.setItem("auth_token", "ABC123"); - const spy = jest.spyOn(authActions, 'fetchCurrentUser').mockImplementation(() => { - return (dispatch) => { - dispatch({ type: 'LOAD_USER_ACTION' }); - return Promise.resolve(); - }; - }); + const spy = jest + .spyOn(authActions, "fetchCurrentUser") + .mockImplementation(() => { + return (dispatch) => { + dispatch({ type: "LOAD_USER_ACTION" }); + return Promise.resolve(); + }; + }); const application = connectedComponent(ConnectedApp, { mockStore }); mount(application); expect(spy).toHaveBeenCalled(); }); - it('does not load the current user if is it already loaded', () => { - local.setItem('auth_token', 'ABC123'); + it("does not load the current user if is it already loaded", () => { + local.setItem("auth_token", "ABC123"); - const spy = jest.spyOn(authActions, 'fetchCurrentUser').mockImplementation(() => { - return { type: 'LOAD_USER_ACTION' }; - }); + const spy = jest + .spyOn(authActions, "fetchCurrentUser") + .mockImplementation(() => { + return { type: "LOAD_USER_ACTION" }; + }); const storeWithUser = { app: {}, auth: { user: { id: 1, - email: 'hi@thegnar.co', + email: "hi@thegnar.co", }, }, notifications: {}, }; const mockStoreWithUser = reduxMockStore(storeWithUser); - const application = connectedComponent(ConnectedApp, { mockStore: mockStoreWithUser }); + const application = connectedComponent(ConnectedApp, { + mockStore: mockStoreWithUser, + }); mount(application); expect(spy).not.toHaveBeenCalled(); }); - it('does not load the current user if there is no auth token', () => { + it("does not load the current user if there is no auth token", () => { local.clear(); - const spy = jest.spyOn(authActions, 'fetchCurrentUser').mockImplementation(() => { - throw new Error('should not have been called'); - }); + const spy = jest + .spyOn(authActions, "fetchCurrentUser") + .mockImplementation(() => { + throw new Error("should not have been called"); + }); const application = connectedComponent(ConnectedApp, { mockStore }); mount(application); diff --git a/frontend/components/App/index.js b/frontend/components/App/index.js index 9122fa1a9..8ce017e64 100644 --- a/frontend/components/App/index.js +++ b/frontend/components/App/index.js @@ -1 +1 @@ -export { default } from './App'; +export { default } from "./App"; diff --git a/frontend/components/AuthenticatedAdminRoutes/AuthenticatedAdminRoutes.jsx b/frontend/components/AuthenticatedAdminRoutes/AuthenticatedAdminRoutes.jsx index c1c86febb..1d9f74c43 100644 --- a/frontend/components/AuthenticatedAdminRoutes/AuthenticatedAdminRoutes.jsx +++ b/frontend/components/AuthenticatedAdminRoutes/AuthenticatedAdminRoutes.jsx @@ -1,10 +1,10 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import { push } from 'react-router-redux'; +import React, { Component } from "react"; +import PropTypes from "prop-types"; +import { connect } from "react-redux"; +import { push } from "react-router-redux"; -import paths from '../../router/paths'; -import userInterface from '../../interfaces/user'; +import paths from "../../router/paths"; +import userInterface from "../../interfaces/user"; export class AuthenticatedAdminRoutes extends Component { static propTypes = { @@ -13,8 +13,11 @@ export class AuthenticatedAdminRoutes extends Component { user: userInterface, }; - componentWillMount () { - const { dispatch, user: { admin } } = this.props; + componentWillMount() { + const { + dispatch, + user: { admin }, + } = this.props; const { HOME } = paths; if (!admin) { @@ -24,18 +27,14 @@ export class AuthenticatedAdminRoutes extends Component { return false; } - render () { + render() { const { children, user } = this.props; if (!user) { return false; } - return ( - <> - {children} - - ); + return <>{children}; } } diff --git a/frontend/components/AuthenticatedAdminRoutes/AuthenticatedAdminRoutes.tests.js b/frontend/components/AuthenticatedAdminRoutes/AuthenticatedAdminRoutes.tests.js index 01f488669..783968d10 100644 --- a/frontend/components/AuthenticatedAdminRoutes/AuthenticatedAdminRoutes.tests.js +++ b/frontend/components/AuthenticatedAdminRoutes/AuthenticatedAdminRoutes.tests.js @@ -1,35 +1,31 @@ -import { mount } from 'enzyme'; +import { mount } from "enzyme"; -import ConnectedAdminRoutes from './AuthenticatedAdminRoutes'; -import { connectedComponent, reduxMockStore } from '../../test/helpers'; +import ConnectedAdminRoutes from "./AuthenticatedAdminRoutes"; +import { connectedComponent, reduxMockStore } from "../../test/helpers"; -describe('AuthenticatedAdminRoutes - layout', () => { +describe("AuthenticatedAdminRoutes - layout", () => { const redirectToHomeAction = { - type: '@@router/CALL_HISTORY_METHOD', + type: "@@router/CALL_HISTORY_METHOD", payload: { - method: 'push', - args: ['/'], + method: "push", + args: ["/"], }, }; - it('redirects to the homepage if the user is not an admin', () => { + it("redirects to the homepage if the user is not an admin", () => { const user = { id: 1, admin: false }; const storeWithoutAdminUser = { auth: { user } }; const mockStore = reduxMockStore(storeWithoutAdminUser); - mount( - connectedComponent(ConnectedAdminRoutes, { mockStore }), - ); + mount(connectedComponent(ConnectedAdminRoutes, { mockStore })); expect(mockStore.getActions()).toContainEqual(redirectToHomeAction); }); - it('does not redirect if the user is an admin', () => { + it("does not redirect if the user is an admin", () => { const user = { id: 1, admin: true }; const storeWithAdminUser = { auth: { user } }; const mockStore = reduxMockStore(storeWithAdminUser); - mount( - connectedComponent(ConnectedAdminRoutes, { mockStore }), - ); + mount(connectedComponent(ConnectedAdminRoutes, { mockStore })); expect(mockStore.getActions()).not.toContainEqual(redirectToHomeAction); }); diff --git a/frontend/components/AuthenticatedAdminRoutes/index.js b/frontend/components/AuthenticatedAdminRoutes/index.js index 73258f57a..0d306fc47 100644 --- a/frontend/components/AuthenticatedAdminRoutes/index.js +++ b/frontend/components/AuthenticatedAdminRoutes/index.js @@ -1 +1 @@ -export { default } from './AuthenticatedAdminRoutes'; +export { default } from "./AuthenticatedAdminRoutes"; diff --git a/frontend/components/AuthenticatedRoutes/AuthenticatedRoutes.jsx b/frontend/components/AuthenticatedRoutes/AuthenticatedRoutes.jsx index c5f56cecb..e5bdb86a4 100644 --- a/frontend/components/AuthenticatedRoutes/AuthenticatedRoutes.jsx +++ b/frontend/components/AuthenticatedRoutes/AuthenticatedRoutes.jsx @@ -1,13 +1,13 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import { isEqual } from 'lodash'; -import { push } from 'react-router-redux'; +import React, { Component } from "react"; +import PropTypes from "prop-types"; +import { connect } from "react-redux"; +import { isEqual } from "lodash"; +import { push } from "react-router-redux"; -import paths from 'router/paths'; -import redirectLocationInterface from 'interfaces/redirect_location'; -import { setRedirectLocation } from 'redux/nodes/redirectLocation/actions'; -import userInterface from 'interfaces/user'; +import paths from "router/paths"; +import redirectLocationInterface from "interfaces/redirect_location"; +import { setRedirectLocation } from "redux/nodes/redirectLocation/actions"; +import userInterface from "interfaces/user"; export class AuthenticatedRoutes extends Component { static propTypes = { @@ -18,7 +18,7 @@ export class AuthenticatedRoutes extends Component { user: userInterface, }; - componentWillMount () { + componentWillMount() { const { loading, user } = this.props; const { redirectToLogin, redirectToPasswordReset } = this; @@ -33,7 +33,7 @@ export class AuthenticatedRoutes extends Component { return false; } - componentWillReceiveProps (nextProps) { + componentWillReceiveProps(nextProps) { if (isEqual(this.props, nextProps)) return false; const { loading, user } = nextProps; @@ -56,27 +56,23 @@ export class AuthenticatedRoutes extends Component { dispatch(setRedirectLocation(locationBeforeTransitions)); return dispatch(push(LOGIN)); - } + }; redirectToPasswordReset = () => { const { dispatch } = this.props; const { RESET_PASSWORD } = paths; return dispatch(push(RESET_PASSWORD)); - } + }; - render () { + render() { const { children, user } = this.props; if (!user) { return false; } - return ( -
- {children} -
- ); + return
{children}
; } } diff --git a/frontend/components/AuthenticatedRoutes/AuthenticatedRoutes.tests.jsx b/frontend/components/AuthenticatedRoutes/AuthenticatedRoutes.tests.jsx index cbbc01d37..b9e090d12 100644 --- a/frontend/components/AuthenticatedRoutes/AuthenticatedRoutes.tests.jsx +++ b/frontend/components/AuthenticatedRoutes/AuthenticatedRoutes.tests.jsx @@ -1,32 +1,32 @@ -import React from 'react'; -import { mount } from 'enzyme'; -import { Provider } from 'react-redux'; +import React from "react"; +import { mount } from "enzyme"; +import { Provider } from "react-redux"; -import AuthenticatedRoutes from './index'; -import helpers from '../../test/helpers'; +import AuthenticatedRoutes from "./index"; +import helpers from "../../test/helpers"; -describe('AuthenticatedRoutes - component', () => { +describe("AuthenticatedRoutes - component", () => { const redirectToLoginAction = { - type: '@@router/CALL_HISTORY_METHOD', + type: "@@router/CALL_HISTORY_METHOD", payload: { - method: 'push', - args: ['/login'], + method: "push", + args: ["/login"], }, }; const redirectToPasswordResetAction = { - type: '@@router/CALL_HISTORY_METHOD', + type: "@@router/CALL_HISTORY_METHOD", payload: { - method: 'push', - args: ['/login/reset'], + method: "push", + args: ["/login/reset"], }, }; - const renderedText = 'This text was rendered'; + const renderedText = "This text was rendered"; const storeWithUser = { auth: { loading: false, user: { id: 1, - email: 'hi@thegnar.co', + email: "hi@thegnar.co", force_password_reset: false, }, }, @@ -39,7 +39,7 @@ describe('AuthenticatedRoutes - component', () => { loading: false, user: { id: 1, - email: 'hi@thegnar.co', + email: "hi@thegnar.co", force_password_reset: true, }, }, @@ -66,7 +66,7 @@ describe('AuthenticatedRoutes - component', () => { }, }; - it('renders if there is a user in state', () => { + it("renders if there is a user in state", () => { const { reduxMockStore } = helpers; const mockStore = reduxMockStore(storeWithUser); const component = mount( @@ -74,13 +74,13 @@ describe('AuthenticatedRoutes - component', () => {
{renderedText}
- , + ); expect(component.text()).toEqual(renderedText); }); - it('redirects to reset password is force_password_reset is true', () => { + it("redirects to reset password is force_password_reset is true", () => { const { reduxMockStore } = helpers; const mockStore = reduxMockStore(storeWithUserRequiringPwReset); mount( @@ -88,13 +88,15 @@ describe('AuthenticatedRoutes - component', () => {
{renderedText}
- , + ); - expect(mockStore.getActions()).toContainEqual(redirectToPasswordResetAction); + expect(mockStore.getActions()).toContainEqual( + redirectToPasswordResetAction + ); }); - it('redirects to login without a user', () => { + it("redirects to login without a user", () => { const { reduxMockStore } = helpers; const mockStore = reduxMockStore(storeWithoutUser); const component = mount( @@ -102,14 +104,14 @@ describe('AuthenticatedRoutes - component', () => {
{renderedText}
- , + ); expect(mockStore.getActions()).toContainEqual(redirectToLoginAction); expect(component.html()).toBeFalsy(); }); - it('does not redirect to login if the user is loading', () => { + it("does not redirect to login if the user is loading", () => { const { reduxMockStore } = helpers; const mockStore = reduxMockStore(storeLoadingUser); const component = mount( @@ -117,7 +119,7 @@ describe('AuthenticatedRoutes - component', () => {
{renderedText}
- , + ); expect(mockStore.getActions()).not.toContainEqual(redirectToLoginAction); diff --git a/frontend/components/AuthenticatedRoutes/index.js b/frontend/components/AuthenticatedRoutes/index.js index ff7351d4a..16bd38922 100644 --- a/frontend/components/AuthenticatedRoutes/index.js +++ b/frontend/components/AuthenticatedRoutes/index.js @@ -1 +1 @@ -export { default } from './AuthenticatedRoutes'; +export { default } from "./AuthenticatedRoutes"; diff --git a/frontend/components/AuthenticationFormWrapper/AuthenticationFormWrapper.jsx b/frontend/components/AuthenticationFormWrapper/AuthenticationFormWrapper.jsx index d1f475377..6bd2f6c5c 100644 --- a/frontend/components/AuthenticationFormWrapper/AuthenticationFormWrapper.jsx +++ b/frontend/components/AuthenticationFormWrapper/AuthenticationFormWrapper.jsx @@ -1,16 +1,16 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; +import React, { Component } from "react"; +import PropTypes from "prop-types"; -import fleetLogoText from '../../../assets/images/fleet-logo-text-white.svg'; +import fleetLogoText from "../../../assets/images/fleet-logo-text-white.svg"; -const baseClass = 'auth-form-wrapper'; +const baseClass = "auth-form-wrapper"; class AuthenticationFormWrapper extends Component { static propTypes = { children: PropTypes.node, }; - render () { + render() { const { children } = this.props; return ( diff --git a/frontend/components/AuthenticationFormWrapper/index.js b/frontend/components/AuthenticationFormWrapper/index.js index e7e9d304c..61eabb7e2 100644 --- a/frontend/components/AuthenticationFormWrapper/index.js +++ b/frontend/components/AuthenticationFormWrapper/index.js @@ -1 +1 @@ -export { default } from './AuthenticationFormWrapper'; +export { default } from "./AuthenticationFormWrapper"; diff --git a/frontend/components/Avatar/Avatar.tsx b/frontend/components/Avatar/Avatar.tsx index 30fc1bcdd..e4621528f 100644 --- a/frontend/components/Avatar/Avatar.tsx +++ b/frontend/components/Avatar/Avatar.tsx @@ -1,5 +1,5 @@ -import React from 'react'; -import classnames from 'classnames'; +import React from "react"; +import classnames from "classnames"; interface IAvatarUserInterface { gravatarURL: string; @@ -11,23 +11,19 @@ interface IAvatarInterface { user: IAvatarUserInterface; } -const baseClass = 'avatar'; +const baseClass = "avatar"; class Avatar extends React.Component { render(): JSX.Element { const { className, size, user } = this.props; - const isSmall = size !== undefined && size.toLowerCase() === 'small'; + const isSmall = size !== undefined && size.toLowerCase() === "small"; const avatarClasses = classnames(baseClass, className, { [`${baseClass}--${size}`]: isSmall, }); const { gravatarURL } = user; return ( - User Avatar + User Avatar ); } } diff --git a/frontend/components/Avatar/_styles.scss b/frontend/components/Avatar/_styles.scss index f70c2f3c8..45eeaa8b7 100644 --- a/frontend/components/Avatar/_styles.scss +++ b/frontend/components/Avatar/_styles.scss @@ -1,5 +1,6 @@ .avatar { - background: $white url('../assets/images/avatar-default.png') center 100% no-repeat; + background: $white url("../assets/images/avatar-default.png") center 100% + no-repeat; background-size: cover; border-radius: 50%; diff --git a/frontend/components/Avatar/index.js b/frontend/components/Avatar/index.js index 970332796..fe7224f76 100644 --- a/frontend/components/Avatar/index.js +++ b/frontend/components/Avatar/index.js @@ -1 +1 @@ -export { default } from './Avatar.tsx'; +export { default } from "./Avatar.tsx"; diff --git a/frontend/components/ClickOutside/ClickOutside.jsx b/frontend/components/ClickOutside/ClickOutside.jsx index 5591c6558..a593c0e0b 100644 --- a/frontend/components/ClickOutside/ClickOutside.jsx +++ b/frontend/components/ClickOutside/ClickOutside.jsx @@ -1,31 +1,34 @@ -import React, { Component } from 'react'; -import { noop } from 'lodash'; +import React, { Component } from "react"; +import { noop } from "lodash"; -import { handleClickOutside } from './helpers'; +import { handleClickOutside } from "./helpers"; -export default (WrappedComponent, { onOutsideClick = noop, getDOMNode = noop }) => { +export default ( + WrappedComponent, + { onOutsideClick = noop, getDOMNode = noop } +) => { class ClickOutside extends Component { - componentDidMount () { + componentDidMount() { const { componentInstance } = this; const clickHandler = onOutsideClick(componentInstance); const componentNode = getDOMNode(componentInstance); this.handleAction = handleClickOutside(clickHandler, componentNode); - global.document.addEventListener('mousedown', this.handleAction); - global.document.addEventListener('touchStart', this.handleAction); + global.document.addEventListener("mousedown", this.handleAction); + global.document.addEventListener("touchStart", this.handleAction); } - componentWillUnmount () { - global.document.removeEventListener('mousedown', this.handleAction); - global.document.removeEventListener('touchStart', this.handleAction); + componentWillUnmount() { + global.document.removeEventListener("mousedown", this.handleAction); + global.document.removeEventListener("touchStart", this.handleAction); } setInstance = (instance) => { this.componentInstance = instance; - } + }; - render () { + render() { const { setInstance } = this; return ; } diff --git a/frontend/components/ClickOutside/index.js b/frontend/components/ClickOutside/index.js index 59b4ab1a1..c0ce145a8 100644 --- a/frontend/components/ClickOutside/index.js +++ b/frontend/components/ClickOutside/index.js @@ -1 +1 @@ -export { default } from './ClickOutside'; +export { default } from "./ClickOutside"; diff --git a/frontend/components/ClickableTableRow/index.jsx b/frontend/components/ClickableTableRow/index.jsx index 2408ef02d..e098dd071 100644 --- a/frontend/components/ClickableTableRow/index.jsx +++ b/frontend/components/ClickableTableRow/index.jsx @@ -1,9 +1,18 @@ -import React from 'react'; -import PropTypes from 'prop-types'; +import React from "react"; +import PropTypes from "prop-types"; const ClickableTableRow = ({ children, className, onClick, onDoubleClick }) => { /* eslint-disable jsx-a11y/no-static-element-interactions */ - return {children}; + return ( + + {children} + + ); /* eslint-enable jsx-a11y/no-static-element-interactions */ }; diff --git a/frontend/components/ClickableTableRow/index.tests.jsx b/frontend/components/ClickableTableRow/index.tests.jsx index 64c85c2c1..71ab805c5 100644 --- a/frontend/components/ClickableTableRow/index.tests.jsx +++ b/frontend/components/ClickableTableRow/index.tests.jsx @@ -1,7 +1,7 @@ -import React from 'react'; -import { mount } from 'enzyme'; +import React from "react"; +import { mount } from "enzyme"; -import ClickableTableRow from './index'; +import ClickableTableRow from "./index"; const clickSpy = jest.fn(); const dblClickSpy = jest.fn(); @@ -11,16 +11,16 @@ const props = { onDoubleClick: dblClickSpy, }; -describe('ClickableTableRow - component', () => { - it('calls onDblClick when row is double clicked', () => { +describe("ClickableTableRow - component", () => { + it("calls onDblClick when row is double clicked", () => { const queryRow = mount(); - queryRow.find('tr').simulate('doubleclick'); + queryRow.find("tr").simulate("doubleclick"); expect(dblClickSpy).toHaveBeenCalled(); }); - it('calls onSelect when row is clicked', () => { + it("calls onSelect when row is clicked", () => { const queryRow = mount(); - queryRow.find('tr').simulate('click'); + queryRow.find("tr").simulate("click"); expect(clickSpy).toHaveBeenCalled(); }); }); diff --git a/frontend/components/EmailTokenRedirect/EmailTokenRedirect.jsx b/frontend/components/EmailTokenRedirect/EmailTokenRedirect.jsx index a1597b274..ab86ccbe0 100644 --- a/frontend/components/EmailTokenRedirect/EmailTokenRedirect.jsx +++ b/frontend/components/EmailTokenRedirect/EmailTokenRedirect.jsx @@ -1,9 +1,9 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; +import React, { Component } from "react"; +import PropTypes from "prop-types"; +import { connect } from "react-redux"; -import helpers from 'components/EmailTokenRedirect/helpers'; -import userInterface from 'interfaces/user'; +import helpers from "components/EmailTokenRedirect/helpers"; +import userInterface from "interfaces/user"; export class EmailTokenRedirect extends Component { static propTypes = { @@ -12,13 +12,13 @@ export class EmailTokenRedirect extends Component { user: userInterface, }; - componentWillMount () { + componentWillMount() { const { dispatch, token, user } = this.props; return helpers.confirmEmailChange(dispatch, token, user); } - componentWillReceiveProps (nextProps) { + componentWillReceiveProps(nextProps) { const { dispatch, token: newToken, user: newUser } = nextProps; const { token: oldToken, user: oldUser } = this.props; @@ -31,7 +31,7 @@ export class EmailTokenRedirect extends Component { return false; } - render () { + render() { return
; } } diff --git a/frontend/components/EmailTokenRedirect/EmailTokenRedirect.tests.jsx b/frontend/components/EmailTokenRedirect/EmailTokenRedirect.tests.jsx index 9eb1ae871..b00830b14 100644 --- a/frontend/components/EmailTokenRedirect/EmailTokenRedirect.tests.jsx +++ b/frontend/components/EmailTokenRedirect/EmailTokenRedirect.tests.jsx @@ -1,15 +1,20 @@ -import React from 'react'; -import { mount } from 'enzyme'; +import React from "react"; +import { mount } from "enzyme"; -import { connectedComponent, reduxMockStore } from 'test/helpers'; -import ConnectedEmailTokenRedirect, { EmailTokenRedirect } from 'components/EmailTokenRedirect/EmailTokenRedirect'; -import Kolide from 'kolide'; -import { userStub } from 'test/stubs'; +import { connectedComponent, reduxMockStore } from "test/helpers"; +import ConnectedEmailTokenRedirect, { + EmailTokenRedirect, +} from "components/EmailTokenRedirect/EmailTokenRedirect"; +import Kolide from "kolide"; +import { userStub } from "test/stubs"; -describe('EmailTokenRedirect - component', () => { +describe("EmailTokenRedirect - component", () => { beforeEach(() => { - jest.spyOn(Kolide.users, 'confirmEmailChange') - .mockImplementation(() => Promise.resolve({ ...userStub, email: 'new@email.com' })); + jest + .spyOn(Kolide.users, "confirmEmailChange") + .mockImplementation(() => + Promise.resolve({ ...userStub, email: "new@email.com" }) + ); }); const authStore = { @@ -17,39 +22,46 @@ describe('EmailTokenRedirect - component', () => { user: userStub, }, }; - const token = 'KFBR392'; + const token = "KFBR392"; const defaultProps = { params: { token, }, }; - describe('componentWillMount', () => { - it('calls the API when a token and user are present', () => { + describe("componentWillMount", () => { + it("calls the API when a token and user are present", () => { const mockStore = reduxMockStore(authStore); - mount(connectedComponent(ConnectedEmailTokenRedirect, { - mockStore, - props: defaultProps, - })); + mount( + connectedComponent(ConnectedEmailTokenRedirect, { + mockStore, + props: defaultProps, + }) + ); - expect(Kolide.users.confirmEmailChange).toHaveBeenCalledWith(userStub, token); + expect(Kolide.users.confirmEmailChange).toHaveBeenCalledWith( + userStub, + token + ); }); - it('does not call the API when only a token is present', () => { + it("does not call the API when only a token is present", () => { const mockStore = reduxMockStore({ auth: {} }); - mount(connectedComponent(ConnectedEmailTokenRedirect, { - mockStore, - props: defaultProps, - })); + mount( + connectedComponent(ConnectedEmailTokenRedirect, { + mockStore, + props: defaultProps, + }) + ); expect(Kolide.users.confirmEmailChange).not.toHaveBeenCalled(); }); }); - describe('componentWillReceiveProps', () => { - it('calls the API when a user is received', () => { + describe("componentWillReceiveProps", () => { + it("calls the API when a user is received", () => { const mockStore = reduxMockStore(); const props = { dispatch: mockStore.dispatch, token }; const Component = mount(); @@ -58,7 +70,10 @@ describe('EmailTokenRedirect - component', () => { Component.setProps({ user: userStub }); - expect(Kolide.users.confirmEmailChange).toHaveBeenCalledWith(userStub, token); + expect(Kolide.users.confirmEmailChange).toHaveBeenCalledWith( + userStub, + token + ); }); }); }); diff --git a/frontend/components/EmailTokenRedirect/helpers.js b/frontend/components/EmailTokenRedirect/helpers.js index 38962f916..26bc4eceb 100644 --- a/frontend/components/EmailTokenRedirect/helpers.js +++ b/frontend/components/EmailTokenRedirect/helpers.js @@ -1,14 +1,14 @@ -import PATHS from 'router/paths'; -import { push } from 'react-router-redux'; -import { renderFlash } from 'redux/nodes/notifications/actions'; -import userActions from 'redux/nodes/entities/users/actions'; +import PATHS from "router/paths"; +import { push } from "react-router-redux"; +import { renderFlash } from "redux/nodes/notifications/actions"; +import userActions from "redux/nodes/entities/users/actions"; const confirmEmailChange = (dispatch, token, user) => { if (user && token) { return dispatch(userActions.confirmEmailChange(user, token)) .then(() => { dispatch(push(PATHS.USER_SETTINGS)); - dispatch(renderFlash('success', 'Email updated successfully!')); + dispatch(renderFlash("success", "Email updated successfully!")); return false; }) diff --git a/frontend/components/EmailTokenRedirect/helpers.tests.js b/frontend/components/EmailTokenRedirect/helpers.tests.js index a4e9a03e6..8e3799ad2 100644 --- a/frontend/components/EmailTokenRedirect/helpers.tests.js +++ b/frontend/components/EmailTokenRedirect/helpers.tests.js @@ -1,65 +1,68 @@ -import { reduxMockStore } from 'test/helpers'; +import { reduxMockStore } from "test/helpers"; -import helpers from 'components/EmailTokenRedirect/helpers'; -import Kolide from 'kolide'; -import { userStub } from 'test/stubs'; +import helpers from "components/EmailTokenRedirect/helpers"; +import Kolide from "kolide"; +import { userStub } from "test/stubs"; -describe('EmailTokenRedirect - helpers', () => { - describe('#confirmEmailChage', () => { +describe("EmailTokenRedirect - helpers", () => { + describe("#confirmEmailChage", () => { const { confirmEmailChange } = helpers; - const token = 'KFBR392'; + const token = "KFBR392"; const authStore = { auth: { user: userStub, }, }; - describe('successfully dispatching the confirmEmailChange action', () => { + describe("successfully dispatching the confirmEmailChange action", () => { beforeEach(() => { - jest.spyOn(Kolide.users, 'confirmEmailChange') - .mockImplementation(() => Promise.resolve({ ...userStub, email: 'new@email.com' })); + jest + .spyOn(Kolide.users, "confirmEmailChange") + .mockImplementation(() => + Promise.resolve({ ...userStub, email: "new@email.com" }) + ); }); - it('pushes the user to the settings page', () => { + it("pushes the user to the settings page", () => { const mockStore = reduxMockStore(authStore); const { dispatch } = mockStore; - return confirmEmailChange(dispatch, userStub, token) - .then(() => { - const dispatchedActions = mockStore.getActions(); + return confirmEmailChange(dispatch, userStub, token).then(() => { + const dispatchedActions = mockStore.getActions(); - expect(dispatchedActions).toContainEqual({ - type: '@@router/CALL_HISTORY_METHOD', - payload: { - method: 'push', - args: ['/profile'], - }, - }); + expect(dispatchedActions).toContainEqual({ + type: "@@router/CALL_HISTORY_METHOD", + payload: { + method: "push", + args: ["/profile"], + }, }); + }); }); }); - describe('unsuccessfully dispatching the confirmEmailChange action', () => { + describe("unsuccessfully dispatching the confirmEmailChange action", () => { beforeEach(() => { const errors = [ { - name: 'base', - reason: 'Unable to confirm your email address', + name: "base", + reason: "Unable to confirm your email address", }, ]; const errorResponse = { status: 422, message: { - message: 'Unable to confirm email address', + message: "Unable to confirm email address", errors, }, }; - jest.spyOn(Kolide.users, 'confirmEmailChange') + jest + .spyOn(Kolide.users, "confirmEmailChange") .mockImplementation(() => Promise.reject(errorResponse)); }); - it('pushes the user to the login page', () => { + it("pushes the user to the login page", () => { const mockStore = reduxMockStore(authStore); const { dispatch } = mockStore; @@ -69,18 +72,18 @@ describe('EmailTokenRedirect - helpers', () => { const dispatchedActions = mockStore.getActions(); expect(dispatchedActions).toContainEqual({ - type: '@@router/CALL_HISTORY_METHOD', + type: "@@router/CALL_HISTORY_METHOD", payload: { - method: 'push', - args: ['/login'], + method: "push", + args: ["/login"], }, }); }); }); }); - describe('when the user or token are not present', () => { - it('does not dispatch any actions when the user is not present', (done) => { + describe("when the user or token are not present", () => { + it("does not dispatch any actions when the user is not present", (done) => { const mockStore = reduxMockStore(authStore); const { dispatch } = mockStore; @@ -95,7 +98,7 @@ describe('EmailTokenRedirect - helpers', () => { .catch(done); }); - it('does not dispatch any actions when the token is not present', (done) => { + it("does not dispatch any actions when the token is not present", (done) => { const mockStore = reduxMockStore(authStore); const { dispatch } = mockStore; diff --git a/frontend/components/EmailTokenRedirect/index.js b/frontend/components/EmailTokenRedirect/index.js index bc5987d04..e045252b0 100644 --- a/frontend/components/EmailTokenRedirect/index.js +++ b/frontend/components/EmailTokenRedirect/index.js @@ -1 +1 @@ -export { default } from './EmailTokenRedirect'; +export { default } from "./EmailTokenRedirect"; diff --git a/frontend/components/EnsureUnauthenticated/EnsureUnauthenticated.jsx b/frontend/components/EnsureUnauthenticated/EnsureUnauthenticated.jsx index 0eebf9764..7be014069 100644 --- a/frontend/components/EnsureUnauthenticated/EnsureUnauthenticated.jsx +++ b/frontend/components/EnsureUnauthenticated/EnsureUnauthenticated.jsx @@ -1,11 +1,11 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import { noop } from 'lodash'; -import { push } from 'react-router-redux'; +import React, { Component } from "react"; +import PropTypes from "prop-types"; +import { connect } from "react-redux"; +import { noop } from "lodash"; +import { push } from "react-router-redux"; -import paths from 'router/paths'; -import userInterface from 'interfaces/user'; +import paths from "router/paths"; +import userInterface from "interfaces/user"; export default (WrappedComponent) => { class EnsureUnauthenticated extends Component { @@ -19,7 +19,7 @@ export default (WrappedComponent) => { dispatch: noop, }; - componentWillMount () { + componentWillMount() { const { currentUser, dispatch } = this.props; const { HOME } = paths; @@ -30,7 +30,7 @@ export default (WrappedComponent) => { return false; } - componentWillReceiveProps (nextProps) { + componentWillReceiveProps(nextProps) { const { currentUser, dispatch } = nextProps; const { HOME } = paths; @@ -41,7 +41,7 @@ export default (WrappedComponent) => { return false; } - render () { + render() { const { currentUser, isLoadingUser } = this.props; if (isLoadingUser || currentUser) { diff --git a/frontend/components/EnsureUnauthenticated/index.js b/frontend/components/EnsureUnauthenticated/index.js index df13339f0..7b208a30d 100644 --- a/frontend/components/EnsureUnauthenticated/index.js +++ b/frontend/components/EnsureUnauthenticated/index.js @@ -1 +1 @@ -export { default } from './EnsureUnauthenticated'; +export { default } from "./EnsureUnauthenticated"; diff --git a/frontend/components/IconToolTip/IconToolTip.tsx b/frontend/components/IconToolTip/IconToolTip.tsx index 6accd1d39..eb9d3f02c 100644 --- a/frontend/components/IconToolTip/IconToolTip.tsx +++ b/frontend/components/IconToolTip/IconToolTip.tsx @@ -1,5 +1,5 @@ -import React from 'react'; -import ReactTooltip from 'react-tooltip'; +import React from "react"; +import ReactTooltip from "react-tooltip"; interface IIconToolTipProps { text: string; @@ -12,13 +12,26 @@ const IconToolTip = (props: IIconToolTipProps): JSX.Element => { return (
- + - + {/* same colour as $core-dark-blue-grey */} - +
); }; diff --git a/frontend/components/IconToolTip/index.ts b/frontend/components/IconToolTip/index.ts index b4f1c9e92..9a55924c0 100644 --- a/frontend/components/IconToolTip/index.ts +++ b/frontend/components/IconToolTip/index.ts @@ -1 +1 @@ -export { default } from './IconToolTip'; +export { default } from "./IconToolTip"; diff --git a/frontend/components/KolideAce/KolideAce.jsx b/frontend/components/KolideAce/KolideAce.jsx index aa55d56d9..457ffc6d0 100644 --- a/frontend/components/KolideAce/KolideAce.jsx +++ b/frontend/components/KolideAce/KolideAce.jsx @@ -1,15 +1,15 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import AceEditor from 'react-ace'; -import classnames from 'classnames'; -import 'brace/mode/sql'; -import 'brace/ext/linking'; -import 'brace/ext/language_tools'; +import React, { Component } from "react"; +import PropTypes from "prop-types"; +import AceEditor from "react-ace"; +import classnames from "classnames"; +import "brace/mode/sql"; +import "brace/ext/linking"; +import "brace/ext/language_tools"; -import './mode'; -import './theme'; +import "./mode"; +import "./theme"; -const baseClass = 'kolide-ace'; +const baseClass = "kolide-ace"; class KolideAce extends Component { static propTypes = { @@ -30,7 +30,7 @@ class KolideAce extends Component { static defaultProps = { fontSize: 14, - name: 'query-editor', + name: "query-editor", showGutter: true, wrapEnabled: false, }; @@ -42,10 +42,8 @@ class KolideAce extends Component { [`${baseClass}__label--error`]: error, }); - return ( -

{error || label}

- ); - } + return

{error || label}

; + }; renderHint = () => { const { hint } = this.props; @@ -55,9 +53,9 @@ class KolideAce extends Component { } return false; - } + }; - render () { + render() { const { error, fontSize, @@ -78,7 +76,7 @@ class KolideAce extends Component { }); const fixHotkeys = (editor) => { - editor.commands.removeCommands(['gotoline', 'find']); + editor.commands.removeCommands(["gotoline", "find"]); onLoad && onLoad(editor); }; @@ -104,11 +102,13 @@ class KolideAce extends Component { value={value} width="100%" wrapEnabled={wrapEnabled} - commands={[{ - name: 'commandName', - bindKey: { win: 'Ctrl-Enter', mac: 'Ctrl-Enter' }, - exec: handleSubmit, - }]} + commands={[ + { + name: "commandName", + bindKey: { win: "Ctrl-Enter", mac: "Ctrl-Enter" }, + exec: handleSubmit, + }, + ]} /> {renderHint()}
diff --git a/frontend/components/KolideAce/_styles.scss b/frontend/components/KolideAce/_styles.scss index 03f019daf..39abb791b 100644 --- a/frontend/components/KolideAce/_styles.scss +++ b/frontend/components/KolideAce/_styles.scss @@ -29,7 +29,7 @@ color: $core-blue; background-color: $ui-light-grey; padding: 2px; - font-family: 'SourceCodePro', $monospace; + font-family: "SourceCodePro", $monospace; } } } diff --git a/frontend/components/KolideAce/index.js b/frontend/components/KolideAce/index.js index ea2dc7bcb..9e9d6cef0 100644 --- a/frontend/components/KolideAce/index.js +++ b/frontend/components/KolideAce/index.js @@ -1 +1 @@ -export { default } from './KolideAce'; +export { default } from "./KolideAce"; diff --git a/frontend/components/KolideAce/mode.js b/frontend/components/KolideAce/mode.js index c9528b566..8b91afda5 100644 --- a/frontend/components/KolideAce/mode.js +++ b/frontend/components/KolideAce/mode.js @@ -1,105 +1,138 @@ /* eslint-disable */ -import { osqueryTableNames } from 'utilities/osquery_tables'; +import { osqueryTableNames } from "utilities/osquery_tables"; -ace.define("ace/mode/kolide_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/sql_highlight_rules"], function(acequire, exports, module) { - "use strict"; +ace.define( + "ace/mode/kolide_highlight_rules", + [ + "require", + "exports", + "module", + "ace/lib/oop", + "ace/mode/sql_highlight_rules", + ], + function (acequire, exports, module) { + "use strict"; - var oop = acequire("../lib/oop"); - var SqlHighlightRules = acequire("./sql_highlight_rules").SqlHighlightRules; + var oop = acequire("../lib/oop"); + var SqlHighlightRules = acequire("./sql_highlight_rules").SqlHighlightRules; - var KolideHighlightRules = function() { - var keywords = ( + var KolideHighlightRules = function () { + var keywords = "select|insert|update|delete|from|where|and|or|group|by|order|limit|offset|having|as|case|" + "when|else|end|type|left|right|join|on|outer|desc|asc|union|create|table|primary|key|if|" + - "foreign|not|references|default|null|inner|cross|natural|database|drop|grant" - ); + "foreign|not|references|default|null|inner|cross|natural|database|drop|grant"; - var builtinConstants = ( - "true|false" - ); + var builtinConstants = "true|false"; - var builtinFunctions = ( + var builtinFunctions = "avg|count|first|last|max|min|sum|ucase|lcase|mid|len|round|rank|now|format|" + - "coalesce|ifnull|isnull|nvl" - ); + "coalesce|ifnull|isnull|nvl"; - var dataTypes = ( + var dataTypes = "int|numeric|decimal|date|varchar|char|bigint|float|double|bit|binary|text|set|timestamp|" + - "money|real|number|integer" - ); + "money|real|number|integer"; - var osqueryTables = osqueryTableNames.join('|'); + var osqueryTables = osqueryTableNames.join("|"); - var keywordMapper = this.createKeywordMapper({ - "osquery-token": osqueryTables, - "support.function": builtinFunctions, - "keyword": keywords, - "constant.language": builtinConstants, - "storage.type": dataTypes, - }, "identifier", true); + var keywordMapper = this.createKeywordMapper( + { + "osquery-token": osqueryTables, + "support.function": builtinFunctions, + keyword: keywords, + "constant.language": builtinConstants, + "storage.type": dataTypes, + }, + "identifier", + true + ); - this.$rules = { - "start" : [{ - token : "comment", - regex : "--.*$" - }, { - token : "comment", - start : "/\\*", - end : "\\*/" - }, { - token : "string", // " string - regex : '".*?"' - }, { - token : "string", // ' string - regex : "'.*?'" - }, { - token : "constant.numeric", // float - regex : "[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b" - }, { - token : keywordMapper, - regex : "[a-zA-Z_$][a-zA-Z0-9_$]*\\b" - }, { - token : "keyword.operator", - regex : "\\+|\\-|\\/|\\/\\/|%|<@>|@>|<@|&|\\^|~|<|>|<=|=>|==|!=|<>|=" - }, { - token : "paren.lparen", - regex : "[\\(]" - }, { - token : "paren.rparen", - regex : "[\\)]" - }, { - token : "text", - regex : "\\s+" - }] + this.$rules = { + start: [ + { + token: "comment", + regex: "--.*$", + }, + { + token: "comment", + start: "/\\*", + end: "\\*/", + }, + { + token: "string", // " string + regex: '".*?"', + }, + { + token: "string", // ' string + regex: "'.*?'", + }, + { + token: "constant.numeric", // float + regex: "[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b", + }, + { + token: keywordMapper, + regex: "[a-zA-Z_$][a-zA-Z0-9_$]*\\b", + }, + { + token: "keyword.operator", + regex: + "\\+|\\-|\\/|\\/\\/|%|<@>|@>|<@|&|\\^|~|<|>|<=|=>|==|!=|<>|=", + }, + { + token: "paren.lparen", + regex: "[\\(]", + }, + { + token: "paren.rparen", + regex: "[\\)]", + }, + { + token: "text", + regex: "\\s+", + }, + ], + }; + + this.normalizeRules(); }; - this.normalizeRules(); - }; + oop.inherits(KolideHighlightRules, SqlHighlightRules); - oop.inherits(KolideHighlightRules, SqlHighlightRules); + exports.KolideHighlightRules = KolideHighlightRules; + } +); - exports.KolideHighlightRules = KolideHighlightRules; -}); +ace.define( + "ace/mode/kolide", + [ + "require", + "exports", + "module", + "ace/lib/oop", + "ace/mode/sql", + "ace/mode/kolide_highlight_rules", + "ace/range", + ], + function (acequire, exports, module) { + "use strict"; -ace.define("ace/mode/kolide",["require","exports","module","ace/lib/oop","ace/mode/sql","ace/mode/kolide_highlight_rules","ace/range"], function(acequire, exports, module) { - "use strict"; + var oop = acequire("../lib/oop"); + var TextMode = acequire("./sql").Mode; + var KolideHighlightRules = acequire("./kolide_highlight_rules") + .KolideHighlightRules; + var Range = acequire("../range").Range; - var oop = acequire("../lib/oop"); - var TextMode = acequire("./sql").Mode; - var KolideHighlightRules = acequire("./kolide_highlight_rules").KolideHighlightRules; - var Range = acequire("../range").Range; + var Mode = function () { + this.HighlightRules = KolideHighlightRules; + }; + oop.inherits(Mode, TextMode); - var Mode = function() { - this.HighlightRules = KolideHighlightRules; - }; - oop.inherits(Mode, TextMode); + (function () { + this.lineCommentStart = "--"; - (function() { + this.$id = "ace/mode/kolide"; + }.call(Mode.prototype)); - this.lineCommentStart = "--"; - - this.$id = "ace/mode/kolide"; - }).call(Mode.prototype); - - exports.Mode = Mode; -}); + exports.Mode = Mode; + } +); diff --git a/frontend/components/KolideAce/theme.css b/frontend/components/KolideAce/theme.css index 9a74d0176..e3b3f9005 100644 --- a/frontend/components/KolideAce/theme.css +++ b/frontend/components/KolideAce/theme.css @@ -1,10 +1,10 @@ .ace_editor.ace-kolide { - font-family: 'SourceCodePro', monospace; + font-family: "SourceCodePro", monospace; font-size: 14px; - background-color: #FAFAFA; + background-color: #fafafa; color: #66696f; border-radius: 4px; - border: solid 1px #DBE3E5; + border: solid 1px #dbe3e5; line-height: 24px; } @@ -21,7 +21,7 @@ } .ace-kolide.ace_autocomplete .ace_content { - padding-left: 0px; + padding-left: 0px; } .ace-kolide .ace_content { @@ -33,7 +33,7 @@ background: #fff; color: #c38dec; z-index: 1; - border-right: solid 1px #E3E3E3; + border-right: solid 1px #e3e3e3; } .ace-kolide .ace_gutter-active-line { @@ -55,12 +55,12 @@ } .ace-kolide .ace_cursor { - color: #aeafad + color: #aeafad; } /* Hide cursor in read-only mode */ .ace-kolide .ace_hidden-cursors { - opacity:0 + opacity: 0; } .ace-kolide .ace_marker-layer .ace_selection { @@ -72,20 +72,20 @@ } .ace-kolide .ace_marker-layer .ace_step { - background: rgb(255, 255, 0) + background: rgb(255, 255, 0); } .ace-kolide .ace_marker-layer .ace_bracket { margin: -1px 0 0 -1px; - border: 1px solid #d1d1d1 + border: 1px solid #d1d1d1; } .ace-kolide .ace_marker-layer .ace_selected-word { - border: 1px solid #d6d6d6 + border: 1px solid #d6d6d6; } .ace-kolide .ace_invisible { - color: #d1d1d1 + color: #d1d1d1; } .ace-kolide .ace_keyword { @@ -93,7 +93,7 @@ font-weight: 600; } -.ace-kolide .ace_osquery-token{ +.ace-kolide .ace_osquery-token { border-radius: 3px; background-color: #ae6ddf; color: #ffffff; @@ -111,11 +111,11 @@ .ace-kolide .ace_storage, .ace-kolide .ace_storage.ace_type, .ace-kolide .ace_support.ace_type { - color: #8959a8 + color: #8959a8; } .ace-kolide .ace_keyword.ace_operator { - color: #3e999f + color: #3e999f; } .ace-kolide .ace_constant.ace_character, @@ -124,43 +124,43 @@ .ace-kolide .ace_keyword.ace_other.ace_unit, .ace-kolide .ace_support.ace_constant, .ace-kolide .ace_variable.ace_parameter { - color: #f5871f + color: #f5871f; } .ace-kolide .ace_constant.ace_other { - color: #666969 + color: #666969; } .ace-kolide .ace_invalid { color: #ffffff; - background-color: #c82829 + background-color: #c82829; } .ace-kolide .ace_invalid.ace_deprecated { color: #ffffff; - background-color: #ae6ddf + background-color: #ae6ddf; } .ace-kolide .ace_fold { background-color: #4271ae; - border-color: #4d4d4c + border-color: #4d4d4c; } .ace-kolide .ace_entity.ace_name.ace_function, .ace-kolide .ace_support.ace_function, .ace-kolide .ace_variable { - color: #4271ae + color: #4271ae; } .ace-kolide .ace_support.ace_class, .ace-kolide .ace_support.ace_type { - color: #c99e00 + color: #c99e00; } .ace-kolide .ace_heading, .ace-kolide .ace_markup.ace_heading, .ace-kolide .ace_string { - color: #4fd061 + color: #4fd061; } .ace-kolide .ace_entity.ace_name.ace_tag, @@ -168,13 +168,14 @@ .ace-kolide .ace_meta.ace_tag, .ace-kolide .ace_string.ace_regexp, .ace-kolide .ace_variable { - color: #c82829 + color: #c82829; } .ace-kolide .ace_comment { - color: #8e908c + color: #8e908c; } .ace-kolide .ace_indent-guide { - background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bdu3f/BwAlfgctduB85QAAAABJRU5ErkJggg==) right repeat-y + background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bdu3f/BwAlfgctduB85QAAAABJRU5ErkJggg==) + right repeat-y; } diff --git a/frontend/components/KolideAce/theme.js b/frontend/components/KolideAce/theme.js index 8c991c604..ac219e50f 100644 --- a/frontend/components/KolideAce/theme.js +++ b/frontend/components/KolideAce/theme.js @@ -1,10 +1,13 @@ /* eslint-disable */ -ace.define("ace/theme/kolide",["require","exports","module","ace/lib/dom"], function(acequire, exports, module) { +ace.define( + "ace/theme/kolide", + ["require", "exports", "module", "ace/lib/dom"], + function (acequire, exports, module) { + exports.isDark = false; + exports.cssClass = "ace-kolide"; + exports.cssText = require("./theme.css"); - exports.isDark = false; - exports.cssClass = "ace-kolide"; - exports.cssText = require('./theme.css'); - -var dom = acequire("../lib/dom"); -dom.importCssString(exports.cssText, exports.cssClass); -}); + var dom = acequire("../lib/dom"); + dom.importCssString(exports.cssText, exports.cssClass); + } +); diff --git a/frontend/components/LoginRoutes/LoginRoutes.jsx b/frontend/components/LoginRoutes/LoginRoutes.jsx index a84e2f010..382ddbd61 100644 --- a/frontend/components/LoginRoutes/LoginRoutes.jsx +++ b/frontend/components/LoginRoutes/LoginRoutes.jsx @@ -1,10 +1,10 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; +import React, { Component } from "react"; +import PropTypes from "prop-types"; +import { connect } from "react-redux"; -import { hideBackgroundImage } from 'redux/nodes/app/actions'; -import { ssoSettings } from 'redux/nodes/auth/actions'; -import LoginPage from 'pages/LoginPage'; +import { hideBackgroundImage } from "redux/nodes/app/actions"; +import { ssoSettings } from "redux/nodes/auth/actions"; +import LoginPage from "pages/LoginPage"; export class LoginRoutes extends Component { static propTypes = { @@ -16,22 +16,21 @@ export class LoginRoutes extends Component { token: PropTypes.string, }; - componentWillMount () { + componentWillMount() { const { dispatch } = this.props; - dispatch(ssoSettings()) - .catch(() => false); + dispatch(ssoSettings()).catch(() => false); dispatch(hideBackgroundImage); } - componentWillUnmount () { + componentWillUnmount() { const { dispatch } = this.props; dispatch(hideBackgroundImage); } - render () { + render() { const { children, isResetPassPage, @@ -42,24 +41,27 @@ export class LoginRoutes extends Component { return (
- {children || + {children || ( } + /> + )}
); } } const mapStateToProps = (state, ownProps) => { - const { location: { pathname, query } } = ownProps; + const { + location: { pathname, query }, + } = ownProps; const { token } = query; - const isForgotPassPage = pathname.endsWith('/login/forgot'); - const isResetPassPage = pathname.endsWith('/login/reset'); + const isForgotPassPage = pathname.endsWith("/login/forgot"); + const isResetPassPage = pathname.endsWith("/login/reset"); return { isForgotPassPage, diff --git a/frontend/components/LoginRoutes/index.js b/frontend/components/LoginRoutes/index.js index cc1be4f81..108a534ac 100644 --- a/frontend/components/LoginRoutes/index.js +++ b/frontend/components/LoginRoutes/index.js @@ -1 +1 @@ -export { default } from './LoginRoutes'; +export { default } from "./LoginRoutes"; diff --git a/frontend/components/NumberPill/NumberPill.jsx b/frontend/components/NumberPill/NumberPill.jsx index 12256896b..95f6f94c9 100644 --- a/frontend/components/NumberPill/NumberPill.jsx +++ b/frontend/components/NumberPill/NumberPill.jsx @@ -1,5 +1,5 @@ -import React from 'react'; -import PropTypes from 'prop-types'; +import React from "react"; +import PropTypes from "prop-types"; const NumberPill = ({ number }) => { return {number}; diff --git a/frontend/components/NumberPill/index.js b/frontend/components/NumberPill/index.js index e07b7d3ce..a5bb2e991 100644 --- a/frontend/components/NumberPill/index.js +++ b/frontend/components/NumberPill/index.js @@ -1 +1 @@ -export { default } from './NumberPill'; +export { default } from "./NumberPill"; diff --git a/frontend/components/Pagination/Pagination.jsx b/frontend/components/Pagination/Pagination.jsx index 7a6a56aca..d3f4d3ba2 100644 --- a/frontend/components/Pagination/Pagination.jsx +++ b/frontend/components/Pagination/Pagination.jsx @@ -1,10 +1,10 @@ -import React, { PureComponent } from 'react'; -import PropTypes from 'prop-types'; +import React, { PureComponent } from "react"; +import PropTypes from "prop-types"; -import Button from 'components/buttons/Button'; -import KolideIcon from 'components/icons/KolideIcon'; +import Button from "components/buttons/Button"; +import KolideIcon from "components/icons/KolideIcon"; -const baseClass = 'pagination'; +const baseClass = "pagination"; class Pagination extends PureComponent { static propTypes = { @@ -16,27 +16,34 @@ class Pagination extends PureComponent { disablePrev = () => { return this.props.currentPage === 0; - } + }; disableNext = () => { // NOTE: not sure why resultsOnCurrentPage is getting assigned undefined. // but this seems to work when there is no data in the table. - return this.props.resultsOnCurrentPage === undefined || - this.props.resultsOnCurrentPage < this.props.resultsPerPage; - } + return ( + this.props.resultsOnCurrentPage === undefined || + this.props.resultsOnCurrentPage < this.props.resultsPerPage + ); + }; - render () { - const { - currentPage, - onPaginationChange, - } = this.props; + render() { + const { currentPage, onPaginationChange } = this.props; return (
- -
diff --git a/frontend/components/Pagination/_style.scss b/frontend/components/Pagination/_style.scss index dad9b19d9..ea32d09b6 100644 --- a/frontend/components/Pagination/_style.scss +++ b/frontend/components/Pagination/_style.scss @@ -1,5 +1,4 @@ .pagination { - &__pager-wrap { display: flex; justify-content: flex-end; diff --git a/frontend/components/Pagination/index.js b/frontend/components/Pagination/index.js index eaef04eb7..34fcdf47a 100644 --- a/frontend/components/Pagination/index.js +++ b/frontend/components/Pagination/index.js @@ -1 +1 @@ -export { default } from './Pagination'; +export { default } from "./Pagination"; diff --git a/frontend/components/StackedWhiteBoxes/StackedWhiteBoxes.jsx b/frontend/components/StackedWhiteBoxes/StackedWhiteBoxes.jsx index 0c5906254..3df2acde2 100644 --- a/frontend/components/StackedWhiteBoxes/StackedWhiteBoxes.jsx +++ b/frontend/components/StackedWhiteBoxes/StackedWhiteBoxes.jsx @@ -1,11 +1,11 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { Link } from 'react-router'; -import classnames from 'classnames'; +import React, { Component } from "react"; +import PropTypes from "prop-types"; +import { Link } from "react-router"; +import classnames from "classnames"; -import KolideIcon from 'components/icons/KolideIcon'; +import KolideIcon from "components/icons/KolideIcon"; -const baseClass = 'stacked-white-boxes'; +const baseClass = "stacked-white-boxes"; class StackedWhiteBoxes extends Component { static propTypes = { @@ -17,7 +17,7 @@ class StackedWhiteBoxes extends Component { previousLocation: PropTypes.string, }; - constructor (props) { + constructor(props) { super(props); this.state = { @@ -27,13 +27,13 @@ class StackedWhiteBoxes extends Component { }; } - componentWillMount () { + componentWillMount() { this.setState({ isLoading: true, }); } - componentDidMount () { + componentDidMount() { const { didLoad } = this; didLoad(); @@ -45,7 +45,7 @@ class StackedWhiteBoxes extends Component { isLoading: false, isLoaded: true, }); - } + }; nowLeaving = (evt) => { const { window } = global; @@ -59,14 +59,13 @@ class StackedWhiteBoxes extends Component { }); if (previousLocation) { - window.setTimeout( - () => { onLeave(previousLocation); }, - 300, - ); + window.setTimeout(() => { + onLeave(previousLocation); + }, 300); } return false; - } + }; renderBackButton = () => { const { previousLocation } = this.props; @@ -76,12 +75,16 @@ class StackedWhiteBoxes extends Component { return (
- +
); - } + }; renderHeader = () => { const { headerText } = this.props; @@ -91,26 +94,18 @@ class StackedWhiteBoxes extends Component {

{headerText}

); - } + }; - render () { + render() { const { children, className, leadText } = this.props; - const { - isLoading, - isLoaded, - isLeaving, - } = this.state; + const { isLoading, isLoaded, isLeaving } = this.state; const { renderBackButton, renderHeader } = this; - const boxClass = classnames( - baseClass, - className, - { - [`${baseClass}--loading`]: isLoading, - [`${baseClass}--loaded`]: isLoaded, - [`${baseClass}--leaving`]: isLeaving, - }, - ); + const boxClass = classnames(baseClass, className, { + [`${baseClass}--loading`]: isLoading, + [`${baseClass}--loaded`]: isLoaded, + [`${baseClass}--leaving`]: isLeaving, + }); return (
diff --git a/frontend/components/StackedWhiteBoxes/index.js b/frontend/components/StackedWhiteBoxes/index.js index e196b503f..a8dbcf543 100644 --- a/frontend/components/StackedWhiteBoxes/index.js +++ b/frontend/components/StackedWhiteBoxes/index.js @@ -1 +1 @@ -export { default } from './StackedWhiteBoxes'; +export { default } from "./StackedWhiteBoxes"; diff --git a/frontend/components/UserRow/UserRow.jsx b/frontend/components/UserRow/UserRow.jsx index b0e259621..a26f1fdf2 100644 --- a/frontend/components/UserRow/UserRow.jsx +++ b/frontend/components/UserRow/UserRow.jsx @@ -1,12 +1,12 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import classnames from 'classnames'; +import React, { Component } from "react"; +import PropTypes from "prop-types"; +import classnames from "classnames"; -import Dropdown from 'components/forms/fields/Dropdown'; -import EditUserForm from 'components/forms/admin/EditUserForm'; -import Modal from 'components/modals/Modal'; -import helpers from 'components/UserRow/helpers'; -import userInterface from 'interfaces/user'; +import Dropdown from "components/forms/fields/Dropdown"; +import EditUserForm from "components/forms/admin/EditUserForm"; +import Modal from "components/modals/Modal"; +import helpers from "components/UserRow/helpers"; +import userInterface from "interfaces/user"; class UserRow extends Component { static propTypes = { @@ -30,28 +30,32 @@ class UserRow extends Component { const { onToggleEditUser, user } = this.props; return onToggleEditUser(user); - } + }; onEditUser = (updatedUser) => { const { onEditUser, user } = this.props; return onEditUser(user, updatedUser); - } + }; onUserActionSelect = (action) => { const { onSelect, onToggleEditUser, user } = this.props; - if (action === 'modify_details') { + if (action === "modify_details") { return onToggleEditUser(user); } return onSelect(user, action); - } + }; renderCTAs = () => { const { isCurrentUser, isInvite, user } = this.props; const { onUserActionSelect } = this; - const userActionOptions = helpers.userActionOptions(isCurrentUser, user, isInvite); + const userActionOptions = helpers.userActionOptions( + isCurrentUser, + user, + isInvite + ); return ( ); - } + }; renderEditUserModal = (isEditing) => { const { userErrors, isCurrentUser, user } = this.props; @@ -70,10 +74,7 @@ class UserRow extends Component { if (isEditing) { return ( - + - - {username} - - - {statusLabel} - + {username} + {statusLabel} {name} {email} {userLabel} diff --git a/frontend/components/UserRow/UserRow.tests.jsx b/frontend/components/UserRow/UserRow.tests.jsx index 7acf3f5de..488eddf49 100644 --- a/frontend/components/UserRow/UserRow.tests.jsx +++ b/frontend/components/UserRow/UserRow.tests.jsx @@ -1,12 +1,12 @@ -import React from 'react'; -import { mount } from 'enzyme'; -import { noop } from 'lodash'; +import React from "react"; +import { mount } from "enzyme"; +import { noop } from "lodash"; -import UserRow from 'components/UserRow/UserRow'; -import { fillInFormInput } from 'test/helpers'; -import { userStub } from 'test/stubs'; +import UserRow from "components/UserRow/UserRow"; +import { fillInFormInput } from "test/helpers"; +import { userStub } from "test/stubs"; -describe('UserRow - component', () => { +describe("UserRow - component", () => { const defaultInviteProps = { isCurrentUser: false, isEditing: false, @@ -23,116 +23,97 @@ describe('UserRow - component', () => { user: userStub, }; - it('renders a user row', () => { + it("renders a user row", () => { const props = { ...defaultUserProps, user: userStub }; const component = mount(); expect(component.length).toEqual(1); - expect(component.find('Dropdown').length).toEqual(1); + expect(component.find("Dropdown").length).toEqual(1); }); - it( - 'calls the onToggleEditUser prop with the user when Modify Details is selected', - () => { - const spy = jest.fn(); - const props = { ...defaultUserProps, onToggleEditUser: spy }; - const component = mount(); + it("calls the onToggleEditUser prop with the user when Modify Details is selected", () => { + const spy = jest.fn(); + const props = { ...defaultUserProps, onToggleEditUser: spy }; + const component = mount(); - component.find('.Select-control').simulate('keyDown', { keyCode: 40 }); - component.find('[aria-label="Modify Details"]').simulate('mousedown'); + component.find(".Select-control").simulate("keyDown", { keyCode: 40 }); + component.find('[aria-label="Modify Details"]').simulate("mousedown"); - expect(spy).toHaveBeenCalledWith(userStub); - }, - ); + expect(spy).toHaveBeenCalledWith(userStub); + }); - it( - 'calls the onSelect prop with the user when Promote User is selected', - () => { - const spy = jest.fn(); - const props = { ...defaultUserProps, onSelect: spy }; - const component = mount(); + it("calls the onSelect prop with the user when Promote User is selected", () => { + const spy = jest.fn(); + const props = { ...defaultUserProps, onSelect: spy }; + const component = mount(); - component.find('.Select-control').simulate('keyDown', { keyCode: 40 }); - component.find('[aria-label="Promote User"]').simulate('mousedown'); + component.find(".Select-control").simulate("keyDown", { keyCode: 40 }); + component.find('[aria-label="Promote User"]').simulate("mousedown"); - expect(spy).toHaveBeenCalledWith(userStub, 'promote_user'); - }, - ); + expect(spy).toHaveBeenCalledWith(userStub, "promote_user"); + }); - it( - 'calls the onSelect prop with the user when Demote User is selected', - () => { - const adminUser = { ...userStub, admin: true }; - const spy = jest.fn(); - const props = { ...defaultUserProps, onSelect: spy, user: adminUser }; - const component = mount(); + it("calls the onSelect prop with the user when Demote User is selected", () => { + const adminUser = { ...userStub, admin: true }; + const spy = jest.fn(); + const props = { ...defaultUserProps, onSelect: spy, user: adminUser }; + const component = mount(); - component.find('.Select-control').simulate('keyDown', { keyCode: 40 }); - component.find('[aria-label="Demote User"]').simulate('mousedown'); + component.find(".Select-control").simulate("keyDown", { keyCode: 40 }); + component.find('[aria-label="Demote User"]').simulate("mousedown"); - expect(spy).toHaveBeenCalledWith(adminUser, 'demote_user'); - }, - ); + expect(spy).toHaveBeenCalledWith(adminUser, "demote_user"); + }); - it( - 'calls the onSelect prop with the user when Disable Account is selected', - () => { - const spy = jest.fn(); - const props = { ...defaultUserProps, onSelect: spy }; - const component = mount(); + it("calls the onSelect prop with the user when Disable Account is selected", () => { + const spy = jest.fn(); + const props = { ...defaultUserProps, onSelect: spy }; + const component = mount(); - component.find('.Select-control').simulate('keyDown', { keyCode: 40 }); - component.find('[aria-label="Disable Account"]').simulate('mousedown'); + component.find(".Select-control").simulate("keyDown", { keyCode: 40 }); + component.find('[aria-label="Disable Account"]').simulate("mousedown"); - expect(spy).toHaveBeenCalledWith(userStub, 'disable_account'); - }, - ); + expect(spy).toHaveBeenCalledWith(userStub, "disable_account"); + }); - it( - 'calls the onSelect prop with the user when Enable Account is selected', - () => { - const disabledUser = { ...userStub, enabled: false }; - const spy = jest.fn(); - const props = { ...defaultUserProps, onSelect: spy, user: disabledUser }; - const component = mount(); + it("calls the onSelect prop with the user when Enable Account is selected", () => { + const disabledUser = { ...userStub, enabled: false }; + const spy = jest.fn(); + const props = { ...defaultUserProps, onSelect: spy, user: disabledUser }; + const component = mount(); - component.find('.Select-control').simulate('keyDown', { keyCode: 40 }); - component.find('[aria-label="Enable Account"]').simulate('mousedown'); + component.find(".Select-control").simulate("keyDown", { keyCode: 40 }); + component.find('[aria-label="Enable Account"]').simulate("mousedown"); - expect(spy).toHaveBeenCalledWith(disabledUser, 'enable_account'); - }, - ); + expect(spy).toHaveBeenCalledWith(disabledUser, "enable_account"); + }); - it( - 'calls the onSelect prop with the user when Require Password Reset is selected', - () => { - const spy = jest.fn(); - const props = { ...defaultUserProps, onSelect: spy }; - const component = mount(); + it("calls the onSelect prop with the user when Require Password Reset is selected", () => { + const spy = jest.fn(); + const props = { ...defaultUserProps, onSelect: spy }; + const component = mount(); - component.find('.Select-control').simulate('keyDown', { keyCode: 40 }); - component.find('[aria-label="Require Password Reset"]').simulate('mousedown'); + component.find(".Select-control").simulate("keyDown", { keyCode: 40 }); + component + .find('[aria-label="Require Password Reset"]') + .simulate("mousedown"); - expect(spy).toHaveBeenCalledWith(userStub, 'reset_password'); - }, - ); + expect(spy).toHaveBeenCalledWith(userStub, "reset_password"); + }); - it( - 'calls the onEditUser prop with the user and updated user when the edit form is submitted', - () => { - const spy = jest.fn(); - const props = { ...defaultUserProps, isEditing: true, onEditUser: spy }; - const component = mount(); - const form = component.find('EditUserForm'); + it("calls the onEditUser prop with the user and updated user when the edit form is submitted", () => { + const spy = jest.fn(); + const props = { ...defaultUserProps, isEditing: true, onEditUser: spy }; + const component = mount(); + const form = component.find("EditUserForm"); - expect(form.length).toEqual(1); + expect(form.length).toEqual(1); - const nameInput = form.find({ name: 'name' }).find('input'); + const nameInput = form.find({ name: "name" }).find("input"); - fillInFormInput(nameInput, 'Foobar'); - form.simulate('submit'); + fillInFormInput(nameInput, "Foobar"); + form.simulate("submit"); - expect(spy).toHaveBeenCalledWith(userStub, { ...userStub, name: 'Foobar' }); - }, - ); + expect(spy).toHaveBeenCalledWith(userStub, { ...userStub, name: "Foobar" }); + }); }); diff --git a/frontend/components/UserRow/_styles.scss b/frontend/components/UserRow/_styles.scss index c05c66e3c..d3264e885 100644 --- a/frontend/components/UserRow/_styles.scss +++ b/frontend/components/UserRow/_styles.scss @@ -1,5 +1,4 @@ .user-row { - &__actions { .form-field--dropdown { margin-bottom: 0; @@ -34,7 +33,7 @@ &:before { background-color: $success; border-radius: 100%; - content: ' '; + content: " "; display: inline-block; height: 8px; margin-right: 8px; @@ -46,7 +45,7 @@ &:before { background-color: $warning; border-radius: 100%; - content: ' '; + content: " "; display: inline-block; height: 8px; margin-right: 8px; @@ -58,7 +57,7 @@ &:before { background-color: $core-red; border-radius: 100%; - content: ' '; + content: " "; display: inline-block; height: 8px; margin-right: 8px; diff --git a/frontend/components/UserRow/helpers.js b/frontend/components/UserRow/helpers.js index 39d702955..b52cd1ab3 100644 --- a/frontend/components/UserRow/helpers.js +++ b/frontend/components/UserRow/helpers.js @@ -1,33 +1,43 @@ const userActionOptions = (isCurrentUser, user, invite) => { const inviteActions = [ - { disabled: false, label: 'Revoke Invitation', value: 'revert_invitation' }, + { disabled: false, label: "Revoke Invitation", value: "revert_invitation" }, ]; const userEnableAction = user.enabled - ? { disabled: isCurrentUser, label: 'Disable Account', value: 'disable_account' } - : { disabled: false, label: 'Enable Account', value: 'enable_account' }; + ? { + disabled: isCurrentUser, + label: "Disable Account", + value: "disable_account", + } + : { disabled: false, label: "Enable Account", value: "enable_account" }; const userPromotionAction = user.admin - ? { disabled: isCurrentUser, label: 'Demote User', value: 'demote_user' } - : { disabled: false, label: 'Promote User', value: 'promote_user' }; + ? { disabled: isCurrentUser, label: "Demote User", value: "demote_user" } + : { disabled: false, label: "Promote User", value: "promote_user" }; if (invite) return inviteActions; - const result = [ - userEnableAction, - userPromotionAction, - ]; + const result = [userEnableAction, userPromotionAction]; if (!user.sso_enabled) { - result.push({ disabled: false, label: 'Require Password Reset', value: 'reset_password', helpText: 'This will revoke all active Fleet API tokens for this user.' }); + result.push({ + disabled: false, + label: "Require Password Reset", + value: "reset_password", + helpText: "This will revoke all active Fleet API tokens for this user.", + }); } - result.push({ disabled: false, label: 'Modify Details', value: 'modify_details' }); + result.push({ + disabled: false, + label: "Modify Details", + value: "modify_details", + }); return result; }; const userStatusLabel = (user, invite) => { if (invite) { - return 'Invited'; + return "Invited"; } - return user.enabled ? 'Active' : 'Disabled'; + return user.enabled ? "Active" : "Disabled"; }; export default { userActionOptions, userStatusLabel }; diff --git a/frontend/components/UserRow/helpers.tests.js b/frontend/components/UserRow/helpers.tests.js index a5201bb3b..86c835c2f 100644 --- a/frontend/components/UserRow/helpers.tests.js +++ b/frontend/components/UserRow/helpers.tests.js @@ -1,74 +1,102 @@ -import helpers from 'components/UserRow/helpers'; -import { userStub } from 'test/stubs'; +import helpers from "components/UserRow/helpers"; +import { userStub } from "test/stubs"; -describe('UserRow - helpers', () => { - describe('#userActionOptions', () => { +describe("UserRow - helpers", () => { + describe("#userActionOptions", () => { const { userActionOptions } = helpers; - it('returns the correct options for invites', () => { + it("returns the correct options for invites", () => { expect(userActionOptions(false, userStub, true)).toEqual([ - { disabled: false, label: 'Revoke Invitation', value: 'revert_invitation' }, + { + disabled: false, + label: "Revoke Invitation", + value: "revert_invitation", + }, ]); }); - it('returns the correct options for an enabled user', () => { + it("returns the correct options for an enabled user", () => { expect(userActionOptions(false, userStub, false)).toEqual([ - { disabled: false, label: 'Disable Account', value: 'disable_account' }, - { disabled: false, label: 'Promote User', value: 'promote_user' }, - { disabled: false, label: 'Require Password Reset', value: 'reset_password', helpText: 'This will revoke all active Fleet API tokens for this user.' }, - { disabled: false, label: 'Modify Details', value: 'modify_details' }, + { disabled: false, label: "Disable Account", value: "disable_account" }, + { disabled: false, label: "Promote User", value: "promote_user" }, + { + disabled: false, + label: "Require Password Reset", + value: "reset_password", + helpText: + "This will revoke all active Fleet API tokens for this user.", + }, + { disabled: false, label: "Modify Details", value: "modify_details" }, ]); }); - it('returns the correct options for a disabled user', () => { + it("returns the correct options for a disabled user", () => { const disabledUser = { ...userStub, enabled: false }; expect(userActionOptions(false, disabledUser, false)).toEqual([ - { disabled: false, label: 'Enable Account', value: 'enable_account' }, - { disabled: false, label: 'Promote User', value: 'promote_user' }, - { disabled: false, label: 'Require Password Reset', value: 'reset_password', helpText: 'This will revoke all active Fleet API tokens for this user.' }, - { disabled: false, label: 'Modify Details', value: 'modify_details' }, + { disabled: false, label: "Enable Account", value: "enable_account" }, + { disabled: false, label: "Promote User", value: "promote_user" }, + { + disabled: false, + label: "Require Password Reset", + value: "reset_password", + helpText: + "This will revoke all active Fleet API tokens for this user.", + }, + { disabled: false, label: "Modify Details", value: "modify_details" }, ]); }); - it('returns the correct options for an admin', () => { + it("returns the correct options for an admin", () => { const adminUser = { ...userStub, admin: true }; expect(userActionOptions(false, adminUser, false)).toEqual([ - { disabled: false, label: 'Disable Account', value: 'disable_account' }, - { disabled: false, label: 'Demote User', value: 'demote_user' }, - { disabled: false, label: 'Require Password Reset', value: 'reset_password', helpText: 'This will revoke all active Fleet API tokens for this user.' }, - { disabled: false, label: 'Modify Details', value: 'modify_details' }, + { disabled: false, label: "Disable Account", value: "disable_account" }, + { disabled: false, label: "Demote User", value: "demote_user" }, + { + disabled: false, + label: "Require Password Reset", + value: "reset_password", + helpText: + "This will revoke all active Fleet API tokens for this user.", + }, + { disabled: false, label: "Modify Details", value: "modify_details" }, ]); }); - it('returns the correct options for the current user', () => { + it("returns the correct options for the current user", () => { const adminUser = { ...userStub, admin: true }; expect(userActionOptions(true, adminUser, false)).toEqual([ - { disabled: true, label: 'Disable Account', value: 'disable_account' }, - { disabled: true, label: 'Demote User', value: 'demote_user' }, - { disabled: false, label: 'Require Password Reset', value: 'reset_password', helpText: 'This will revoke all active Fleet API tokens for this user.' }, - { disabled: false, label: 'Modify Details', value: 'modify_details' }, + { disabled: true, label: "Disable Account", value: "disable_account" }, + { disabled: true, label: "Demote User", value: "demote_user" }, + { + disabled: false, + label: "Require Password Reset", + value: "reset_password", + helpText: + "This will revoke all active Fleet API tokens for this user.", + }, + { disabled: false, label: "Modify Details", value: "modify_details" }, ]); }); }); - describe('#userStatusLabel', () => { + describe("#userStatusLabel", () => { const { userStatusLabel } = helpers; - it('returns the correct options for an invite', () => { - expect(userStatusLabel(userStub, true)).toEqual('Invited'); + it("returns the correct options for an invite", () => { + expect(userStatusLabel(userStub, true)).toEqual("Invited"); }); - it('returns the correct options for an enabled user', () => { - expect(userStatusLabel(userStub, false)).toEqual('Active'); + it("returns the correct options for an enabled user", () => { + expect(userStatusLabel(userStub, false)).toEqual("Active"); }); - it('returns the correct options for a disabled user', () => { + it("returns the correct options for a disabled user", () => { const disabledUser = { ...userStub, enabled: false }; - expect(userStatusLabel(disabledUser, false)).toEqual('Disabled'); + expect(userStatusLabel(disabledUser, false)).toEqual("Disabled"); }); }); }); diff --git a/frontend/components/UserRow/index.js b/frontend/components/UserRow/index.js index 9b41db753..9e646ea5a 100644 --- a/frontend/components/UserRow/index.js +++ b/frontend/components/UserRow/index.js @@ -1 +1 @@ -export { default } from './UserRow'; +export { default } from "./UserRow"; diff --git a/frontend/components/WarningBanner/WarningBanner.jsx b/frontend/components/WarningBanner/WarningBanner.jsx index ac7840a0c..2a3cb64a2 100644 --- a/frontend/components/WarningBanner/WarningBanner.jsx +++ b/frontend/components/WarningBanner/WarningBanner.jsx @@ -1,8 +1,8 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import classnames from 'classnames'; +import React from "react"; +import PropTypes from "prop-types"; +import classnames from "classnames"; -const baseClass = 'warning-banner'; +const baseClass = "warning-banner"; const WarningBanner = ({ children, className, shouldShowWarning }) => { if (!shouldShowWarning) { diff --git a/frontend/components/WarningBanner/WarningBanner.tests.jsx b/frontend/components/WarningBanner/WarningBanner.tests.jsx index 07abf17a8..4871f268f 100644 --- a/frontend/components/WarningBanner/WarningBanner.tests.jsx +++ b/frontend/components/WarningBanner/WarningBanner.tests.jsx @@ -1,11 +1,11 @@ -import React from 'react'; -import { shallow } from 'enzyme'; +import React from "react"; +import { shallow } from "enzyme"; -import WarningBanner from 'components/WarningBanner/WarningBanner'; +import WarningBanner from "components/WarningBanner/WarningBanner"; -describe('WarningBanner - component', () => { - it('renders empty when disabled', () => { - const props = { shouldShowWarning: false, message: 'message' }; +describe("WarningBanner - component", () => { + it("renders empty when disabled", () => { + const props = { shouldShowWarning: false, message: "message" }; const component = shallow(); expect(component.html()).toBe(null); }); diff --git a/frontend/components/WarningBanner/index.js b/frontend/components/WarningBanner/index.js index 44d7c4c2f..c9dba94e7 100644 --- a/frontend/components/WarningBanner/index.js +++ b/frontend/components/WarningBanner/index.js @@ -1 +1 @@ -export { default } from './WarningBanner'; +export { default } from "./WarningBanner"; diff --git a/frontend/components/YamlAce/YamlAce.jsx b/frontend/components/YamlAce/YamlAce.jsx index b780013b7..5e7b5cbc7 100644 --- a/frontend/components/YamlAce/YamlAce.jsx +++ b/frontend/components/YamlAce/YamlAce.jsx @@ -1,11 +1,11 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import AceEditor from 'react-ace'; -import classnames from 'classnames'; +import React, { Component } from "react"; +import PropTypes from "prop-types"; +import AceEditor from "react-ace"; +import classnames from "classnames"; -import 'ace-builds/src-noconflict/mode-yaml'; +import "ace-builds/src-noconflict/mode-yaml"; -const baseClass = 'yaml-ace'; +const baseClass = "yaml-ace"; class YamlAce extends Component { static propTypes = { @@ -15,20 +15,17 @@ class YamlAce extends Component { onChange: PropTypes.func.isRequired, value: PropTypes.string, wrapperClassName: PropTypes.string, - } + }; renderLabel = () => { const { error, label } = this.props; - const labelClassName = classnames( - `${baseClass}__label`, - { [`${baseClass}__label--error`]: error }, - ); + const labelClassName = classnames(`${baseClass}__label`, { + [`${baseClass}__label--error`]: error, + }); - return ( -

{error || label}

- ); - } + return

{error || label}

; + }; render() { const { diff --git a/frontend/components/YamlAce/index.js b/frontend/components/YamlAce/index.js index 03ba88370..0af7f7769 100644 --- a/frontend/components/YamlAce/index.js +++ b/frontend/components/YamlAce/index.js @@ -1 +1 @@ -export { default } from './YamlAce'; +export { default } from "./YamlAce"; diff --git a/frontend/components/buttons/Button/Button.tsx b/frontend/components/buttons/Button/Button.tsx index bfd792253..d8327cdcc 100644 --- a/frontend/components/buttons/Button/Button.tsx +++ b/frontend/components/buttons/Button/Button.tsx @@ -1,7 +1,7 @@ -import React from 'react'; -import classnames from 'classnames'; +import React from "react"; +import classnames from "classnames"; -const baseClass = 'button'; +const baseClass = "button"; interface IButtonProps { autofocus?: boolean; @@ -12,7 +12,7 @@ interface IButtonProps { onClick: (evt: React.MouseEvent) => void; size?: string; tabIndex?: number; - type?: 'button' | 'submit' | 'reset'; + type?: "button" | "submit" | "reset"; title?: string; variant?: string; } @@ -27,14 +27,16 @@ interface Inputs { class Button extends React.Component { static defaultProps = { block: false, - size: '', - type: 'button', - variant: 'default', + size: "", + type: "button", + variant: "default", }; componentDidMount(): void { const { autofocus } = this.props; - const { inputs: { button } } = this; + const { + inputs: { button }, + } = this; if (autofocus && button) { button.focus(); @@ -45,7 +47,7 @@ class Button extends React.Component { this.inputs.button = button; return false; - } + }; inputs: Inputs = {}; @@ -61,16 +63,31 @@ class Button extends React.Component { } return false; - } + }; render(): JSX.Element { const { handleClick, setRef } = this; - const { block, children, className, disabled, size, tabIndex, type, title, variant } = this.props; - const fullClassName = classnames(baseClass, `${baseClass}--${variant}`, className, { - [`${baseClass}--block`]: block, - [`${baseClass}--disabled`]: disabled, - [`${baseClass}--${size}`]: size !== undefined, - }); + const { + block, + children, + className, + disabled, + size, + tabIndex, + type, + title, + variant, + } = this.props; + const fullClassName = classnames( + baseClass, + `${baseClass}--${variant}`, + className, + { + [`${baseClass}--block`]: block, + [`${baseClass}--disabled`]: disabled, + [`${baseClass}--${size}`]: size !== undefined, + } + ); return ( +
  • +
  • ); }; - render () { + render() { const { children, className, @@ -92,7 +101,8 @@ export class DropdownButton extends Component { type={type} variant={variant} > - {children} + {children}{" "} +
      diff --git a/frontend/components/buttons/DropdownButton/DropdownButton.tests.jsx b/frontend/components/buttons/DropdownButton/DropdownButton.tests.jsx index cdb6ea319..0d98a8129 100644 --- a/frontend/components/buttons/DropdownButton/DropdownButton.tests.jsx +++ b/frontend/components/buttons/DropdownButton/DropdownButton.tests.jsx @@ -1,23 +1,28 @@ -import React from 'react'; -import { mount } from 'enzyme'; -import { noop } from 'lodash'; +import React from "react"; +import { mount } from "enzyme"; +import { noop } from "lodash"; -import { DropdownButton } from './DropdownButton'; +import { DropdownButton } from "./DropdownButton"; -describe('DropdownButton - component', () => { +describe("DropdownButton - component", () => { it("calls the clicked item's onClick attribute", () => { const optionSpy = jest.fn(); - const dropdownOptions = [{ label: 'btn1', onClick: noop }, { label: 'btn2', onClick: optionSpy }]; + const dropdownOptions = [ + { label: "btn1", onClick: noop }, + { label: "btn2", onClick: optionSpy }, + ]; const component = mount( - - New Button - , + New Button ); - component.find('button.dropdown-button').simulate('click'); + component.find("button.dropdown-button").simulate("click"); expect(component.state().isOpen).toEqual(true); - component.find('li.dropdown-button__option').last().find('Button').simulate('click'); + component + .find("li.dropdown-button__option") + .last() + .find("Button") + .simulate("click"); expect(optionSpy).toHaveBeenCalled(); }); }); diff --git a/frontend/components/buttons/DropdownButton/index.js b/frontend/components/buttons/DropdownButton/index.js index c9855f342..d06e95af1 100644 --- a/frontend/components/buttons/DropdownButton/index.js +++ b/frontend/components/buttons/DropdownButton/index.js @@ -1 +1 @@ -export { default } from './DropdownButton'; +export { default } from "./DropdownButton"; diff --git a/frontend/components/buttons/EllipsisMenu/EllipsisMenu.jsx b/frontend/components/buttons/EllipsisMenu/EllipsisMenu.jsx index 1d997b665..acfba0cd0 100644 --- a/frontend/components/buttons/EllipsisMenu/EllipsisMenu.jsx +++ b/frontend/components/buttons/EllipsisMenu/EllipsisMenu.jsx @@ -1,10 +1,10 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; +import React, { Component } from "react"; +import PropTypes from "prop-types"; -import { calculateTooltipDirection } from './helpers'; -import ClickOutside from '../../ClickOutside'; +import { calculateTooltipDirection } from "./helpers"; +import ClickOutside from "../../ClickOutside"; -const baseClass = 'ellipsis-menu'; +const baseClass = "ellipsis-menu"; export class EllipsisMenu extends Component { static propTypes = { @@ -12,7 +12,7 @@ export class EllipsisMenu extends Component { positionStyles: PropTypes.object, // eslint-disable-line react/forbid-prop-types }; - constructor (props) { + constructor(props) { super(props); this.state = { @@ -20,18 +20,18 @@ export class EllipsisMenu extends Component { }; } - componentDidMount () { + componentDidMount() { const { setTooltipDirection } = this; - global.window.addEventListener('resize', setTooltipDirection); + global.window.addEventListener("resize", setTooltipDirection); return setTooltipDirection(); } - componentWillUnmount () { + componentWillUnmount() { const { setTooltipDirection } = this; - global.window.removeEventListener('resize', setTooltipDirection); + global.window.removeEventListener("resize", setTooltipDirection); return false; } @@ -42,11 +42,11 @@ export class EllipsisMenu extends Component { this.setState({ showChildren: !showChildren }); return false; - } + }; setDOMNode = (DOMNode) => { this.DOMNode = DOMNode; - } + }; setTooltipDirection = () => { if (this.DOMNode) { @@ -56,12 +56,12 @@ export class EllipsisMenu extends Component { } return false; - } + }; renderChildren = () => { const { children } = this.props; const { showChildren, tooltipDirection } = this.state; - const triangleDirection = tooltipDirection === 'left' ? 'right' : 'left'; + const triangleDirection = tooltipDirection === "left" ? "right" : "left"; if (!showChildren) { return false; @@ -74,18 +74,14 @@ export class EllipsisMenu extends Component { {children}
    ); - } + }; - render () { + render() { const { onToggleChildren, renderChildren, setDOMNode } = this; const { positionStyles } = this.props; return ( -
    +
    - } + )}
    - +
    ); @@ -40,12 +46,12 @@ class ChangeEmailForm extends Component { } export default Form(ChangeEmailForm, { - fields: ['password'], + fields: ["password"], validate: (formData) => { if (!formData.password) { return { valid: false, - errors: { password: 'Password must be present' }, + errors: { password: "Password must be present" }, }; } diff --git a/frontend/components/forms/ChangeEmailForm/index.js b/frontend/components/forms/ChangeEmailForm/index.js index 1c59ce124..2a965bffa 100644 --- a/frontend/components/forms/ChangeEmailForm/index.js +++ b/frontend/components/forms/ChangeEmailForm/index.js @@ -1 +1 @@ -export { default } from './ChangeEmailForm'; +export { default } from "./ChangeEmailForm"; diff --git a/frontend/components/forms/ChangePasswordForm/ChangePasswordForm.jsx b/frontend/components/forms/ChangePasswordForm/ChangePasswordForm.jsx index d66007ebd..e169cd1b5 100644 --- a/frontend/components/forms/ChangePasswordForm/ChangePasswordForm.jsx +++ b/frontend/components/forms/ChangePasswordForm/ChangePasswordForm.jsx @@ -1,14 +1,18 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; +import React, { Component } from "react"; +import PropTypes from "prop-types"; -import Button from 'components/buttons/Button'; -import Form from 'components/forms/Form'; -import formFieldInterface from 'interfaces/form_field'; -import InputField from 'components/forms/fields/InputField'; -import validate from 'components/forms/ChangePasswordForm/validate'; +import Button from "components/buttons/Button"; +import Form from "components/forms/Form"; +import formFieldInterface from "interfaces/form_field"; +import InputField from "components/forms/fields/InputField"; +import validate from "components/forms/ChangePasswordForm/validate"; -const formFields = ['old_password', 'new_password', 'new_password_confirmation']; -const baseClass = 'change-password-form'; +const formFields = [ + "old_password", + "new_password", + "new_password_confirmation", +]; +const baseClass = "change-password-form"; class ChangePasswordForm extends Component { static propTypes = { @@ -21,7 +25,7 @@ class ChangePasswordForm extends Component { onCancel: PropTypes.func.isRequired, }; - render () { + render() { const { fields, handleSubmit, onCancel } = this.props; return ( @@ -43,8 +47,16 @@ class ChangePasswordForm extends Component { type="password" />
    - - + +
    ); @@ -52,4 +64,3 @@ class ChangePasswordForm extends Component { } export default Form(ChangePasswordForm, { fields: formFields, validate }); - diff --git a/frontend/components/forms/ChangePasswordForm/ChangePasswordForm.tests.jsx b/frontend/components/forms/ChangePasswordForm/ChangePasswordForm.tests.jsx index ae38ad233..5f33841ff 100644 --- a/frontend/components/forms/ChangePasswordForm/ChangePasswordForm.tests.jsx +++ b/frontend/components/forms/ChangePasswordForm/ChangePasswordForm.tests.jsx @@ -1,78 +1,112 @@ -import React from 'react'; -import { mount } from 'enzyme'; -import { noop } from 'lodash'; +import React from "react"; +import { mount } from "enzyme"; +import { noop } from "lodash"; -import ChangePasswordForm from 'components/forms/ChangePasswordForm'; -import helpers from 'test/helpers'; +import ChangePasswordForm from "components/forms/ChangePasswordForm"; +import helpers from "test/helpers"; const { fillInFormInput, itBehavesLikeAFormInputElement } = helpers; -describe('ChangePasswordForm - component', () => { - it('has the correct fields', () => { - const form = mount(); +describe("ChangePasswordForm - component", () => { + it("has the correct fields", () => { + const form = mount( + + ); - itBehavesLikeAFormInputElement(form, 'old_password'); - itBehavesLikeAFormInputElement(form, 'new_password'); - itBehavesLikeAFormInputElement(form, 'new_password_confirmation'); + itBehavesLikeAFormInputElement(form, "old_password"); + itBehavesLikeAFormInputElement(form, "new_password"); + itBehavesLikeAFormInputElement(form, "new_password_confirmation"); }); - it('renders the password fields as HTML password fields', () => { - const form = mount(); + it("renders the password fields as HTML password fields", () => { + const form = mount( + + ); const passwordField = form.find('input[name="old_password"]'); const newPasswordField = form.find('input[name="new_password"]'); - const newPasswordConfirmationField = form.find('input[name="new_password_confirmation"]'); + const newPasswordConfirmationField = form.find( + 'input[name="new_password_confirmation"]' + ); - expect(passwordField.prop('type')).toEqual('password'); - expect(newPasswordField.prop('type')).toEqual('password'); - expect(newPasswordConfirmationField.prop('type')).toEqual('password'); + expect(passwordField.prop("type")).toEqual("password"); + expect(newPasswordField.prop("type")).toEqual("password"); + expect(newPasswordConfirmationField.prop("type")).toEqual("password"); }); - it('calls the handleSubmit props with form data', () => { + it("calls the handleSubmit props with form data", () => { const handleSubmitSpy = jest.fn(); - const form = mount().find('form'); - const expectedFormData = { old_password: 'p@ssw0rd', new_password: 'p@ssw0rd1', new_password_confirmation: 'p@ssw0rd1' }; - const passwordInput = form.find({ name: 'old_password' }).find('input'); - const newPasswordInput = form.find({ name: 'new_password' }).find('input'); - const newPasswordConfirmationInput = form.find({ name: 'new_password_confirmation' }).find('input'); + const form = mount( + + ).find("form"); + const expectedFormData = { + old_password: "p@ssw0rd", + new_password: "p@ssw0rd1", + new_password_confirmation: "p@ssw0rd1", + }; + const passwordInput = form.find({ name: "old_password" }).find("input"); + const newPasswordInput = form.find({ name: "new_password" }).find("input"); + const newPasswordConfirmationInput = form + .find({ name: "new_password_confirmation" }) + .find("input"); fillInFormInput(passwordInput, expectedFormData.old_password); fillInFormInput(newPasswordInput, expectedFormData.new_password); - fillInFormInput(newPasswordConfirmationInput, expectedFormData.new_password_confirmation); + fillInFormInput( + newPasswordConfirmationInput, + expectedFormData.new_password_confirmation + ); - form.simulate('submit'); + form.simulate("submit"); expect(handleSubmitSpy).toHaveBeenCalledWith(expectedFormData); }); - it('calls the onCancel prop when CANCEL is clicked', () => { + it("calls the onCancel prop when CANCEL is clicked", () => { const onCancelSpy = jest.fn(); - const form = mount().find('form'); - const cancelBtn = form.find('Button').findWhere(n => n.prop('children') === 'Cancel').find('button'); + const form = mount( + + ).find("form"); + const cancelBtn = form + .find("Button") + .findWhere((n) => n.prop("children") === "Cancel") + .find("button"); - cancelBtn.simulate('click'); + cancelBtn.simulate("click"); expect(onCancelSpy).toHaveBeenCalled(); }); - it('does not submit when the new password is invalid', () => { + it("does not submit when the new password is invalid", () => { const handleSubmitSpy = jest.fn(); - const component = mount(); - const form = component.find('form'); - const expectedFormData = { old_password: 'p@ssw0rd', new_password: 'new_password', new_password_confirmation: 'new_password' }; - const passwordInput = form.find({ name: 'old_password' }).find('input'); - const newPasswordInput = form.find({ name: 'new_password' }).find('input'); - const newPasswordConfirmationInput = form.find({ name: 'new_password_confirmation' }).find('input'); + const component = mount( + + ); + const form = component.find("form"); + const expectedFormData = { + old_password: "p@ssw0rd", + new_password: "new_password", + new_password_confirmation: "new_password", + }; + const passwordInput = form.find({ name: "old_password" }).find("input"); + const newPasswordInput = form.find({ name: "new_password" }).find("input"); + const newPasswordConfirmationInput = form + .find({ name: "new_password_confirmation" }) + .find("input"); fillInFormInput(passwordInput, expectedFormData.old_password); fillInFormInput(newPasswordInput, expectedFormData.new_password); - fillInFormInput(newPasswordConfirmationInput, expectedFormData.new_password_confirmation); + fillInFormInput( + newPasswordConfirmationInput, + expectedFormData.new_password_confirmation + ); - form.simulate('submit'); + form.simulate("submit"); expect(handleSubmitSpy).not.toHaveBeenCalled(); - expect(component.state('errors')).toMatchObject({ - new_password: 'Password must be at least 7 characters and contain at least 1 letter, 1 number, and 1 symbol', + expect(component.state("errors")).toMatchObject({ + new_password: + "Password must be at least 7 characters and contain at least 1 letter, 1 number, and 1 symbol", }); }); }); diff --git a/frontend/components/forms/ChangePasswordForm/index.js b/frontend/components/forms/ChangePasswordForm/index.js index 9c3595650..edf4435e2 100644 --- a/frontend/components/forms/ChangePasswordForm/index.js +++ b/frontend/components/forms/ChangePasswordForm/index.js @@ -1 +1 @@ -export { default } from './ChangePasswordForm'; +export { default } from "./ChangePasswordForm"; diff --git a/frontend/components/forms/ChangePasswordForm/validate.js b/frontend/components/forms/ChangePasswordForm/validate.js index a239ce1a1..56c6e000a 100644 --- a/frontend/components/forms/ChangePasswordForm/validate.js +++ b/frontend/components/forms/ChangePasswordForm/validate.js @@ -1,6 +1,6 @@ -import { size } from 'lodash'; -import validateEquality from 'components/forms/validators/validate_equality'; -import validPassword from 'components/forms/validators/valid_password'; +import { size } from "lodash"; +import validateEquality from "components/forms/validators/validate_equality"; +import validPassword from "components/forms/validators/valid_password"; export default (formData) => { const errors = {}; @@ -11,24 +11,30 @@ export default (formData) => { } = formData; if (newPassword && newPasswordConfirmation && !validPassword(newPassword)) { - errors.new_password = 'Password must be at least 7 characters and contain at least 1 letter, 1 number, and 1 symbol'; + errors.new_password = + "Password must be at least 7 characters and contain at least 1 letter, 1 number, and 1 symbol"; } if (!oldPassword) { - errors.old_password = 'Password must be present'; + errors.old_password = "Password must be present"; } if (!newPassword) { - errors.new_password = 'New password must be present'; + errors.new_password = "New password must be present"; } if (!newPasswordConfirmation) { - errors.new_password_confirmation = 'New password confirmation must be present'; + errors.new_password_confirmation = + "New password confirmation must be present"; } - if (newPassword && newPasswordConfirmation && - !validateEquality(newPassword, newPasswordConfirmation)) { - errors.new_password_confirmation = 'New password confirmation does not match new password'; + if ( + newPassword && + newPasswordConfirmation && + !validateEquality(newPassword, newPasswordConfirmation) + ) { + errors.new_password_confirmation = + "New password confirmation does not match new password"; } const valid = !size(errors); diff --git a/frontend/components/forms/ConfigurePackQueryForm/ConfigurePackQueryForm.jsx b/frontend/components/forms/ConfigurePackQueryForm/ConfigurePackQueryForm.jsx index fb28cead2..8ebd4204b 100644 --- a/frontend/components/forms/ConfigurePackQueryForm/ConfigurePackQueryForm.jsx +++ b/frontend/components/forms/ConfigurePackQueryForm/ConfigurePackQueryForm.jsx @@ -1,54 +1,64 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { pull } from 'lodash'; +import React, { Component } from "react"; +import PropTypes from "prop-types"; +import { pull } from "lodash"; -import KolideIcon from 'components/icons/KolideIcon'; -import Button from 'components/buttons/Button'; -import Dropdown from 'components/forms/fields/Dropdown'; -import Form from 'components/forms/Form'; -import formFieldInterface from 'interfaces/form_field'; -import InputField from 'components/forms/fields/InputField'; -import validate from 'components/forms/ConfigurePackQueryForm/validate'; +import KolideIcon from "components/icons/KolideIcon"; +import Button from "components/buttons/Button"; +import Dropdown from "components/forms/fields/Dropdown"; +import Form from "components/forms/Form"; +import formFieldInterface from "interfaces/form_field"; +import InputField from "components/forms/fields/InputField"; +import validate from "components/forms/ConfigurePackQueryForm/validate"; -const baseClass = 'configure-pack-query-form'; -const fieldNames = ['query_id', 'interval', 'logging_type', 'platform', 'shard', 'version']; +const baseClass = "configure-pack-query-form"; +const fieldNames = [ + "query_id", + "interval", + "logging_type", + "platform", + "shard", + "version", +]; const platformOptions = [ - { label: 'All', value: '' }, - { label: 'Windows', value: 'windows' }, - { label: 'Linux', value: 'linux' }, - { label: 'macOS', value: 'darwin' }, + { label: "All", value: "" }, + { label: "Windows", value: "windows" }, + { label: "Linux", value: "linux" }, + { label: "macOS", value: "darwin" }, ]; const loggingTypeOptions = [ - { label: 'Differential', value: 'differential' }, - { label: 'Differential (Ignore Removals)', value: 'differential_ignore_removals' }, - { label: 'Snapshot', value: 'snapshot' }, + { label: "Differential", value: "differential" }, + { + label: "Differential (Ignore Removals)", + value: "differential_ignore_removals", + }, + { label: "Snapshot", value: "snapshot" }, ]; const minOsqueryVersionOptions = [ - { label: 'All', value: '' }, - { label: '4.7.0 +', value: '4.7.0' }, - { label: '4.6.0 +', value: '4.6.0' }, - { label: '4.5.1 +', value: '4.5.1' }, - { label: '4.5.0 +', value: '4.5.0' }, - { label: '4.4.0 +', value: '4.4.0' }, - { label: '4.3.0 +', value: '4.3.0' }, - { label: '4.2.0 +', value: '4.2.0' }, - { label: '4.1.2 +', value: '4.1.2' }, - { label: '4.1.1 +', value: '4.1.1' }, - { label: '4.1.0 +', value: '4.1.0' }, - { label: '4.0.2 +', value: '4.0.2' }, - { label: '4.0.1 +', value: '4.0.1' }, - { label: '4.0.0 +', value: '4.0.0' }, - { label: '3.4.0 +', value: '3.4.0' }, - { label: '3.3.2 +', value: '3.3.2' }, - { label: '3.3.1 +', value: '3.3.1' }, - { label: '3.2.6 +', value: '3.2.6' }, - { label: '2.2.1 +', value: '2.2.1' }, - { label: '2.2.0 +', value: '2.2.0' }, - { label: '2.1.2 +', value: '2.1.2' }, - { label: '2.1.1 +', value: '2.1.1' }, - { label: '2.0.0 +', value: '2.0.0' }, - { label: '1.8.2 +', value: '1.8.2' }, - { label: '1.8.1 +', value: '1.8.1' }, + { label: "All", value: "" }, + { label: "4.7.0 +", value: "4.7.0" }, + { label: "4.6.0 +", value: "4.6.0" }, + { label: "4.5.1 +", value: "4.5.1" }, + { label: "4.5.0 +", value: "4.5.0" }, + { label: "4.4.0 +", value: "4.4.0" }, + { label: "4.3.0 +", value: "4.3.0" }, + { label: "4.2.0 +", value: "4.2.0" }, + { label: "4.1.2 +", value: "4.1.2" }, + { label: "4.1.1 +", value: "4.1.1" }, + { label: "4.1.0 +", value: "4.1.0" }, + { label: "4.0.2 +", value: "4.0.2" }, + { label: "4.0.1 +", value: "4.0.1" }, + { label: "4.0.0 +", value: "4.0.0" }, + { label: "3.4.0 +", value: "3.4.0" }, + { label: "3.3.2 +", value: "3.3.2" }, + { label: "3.3.1 +", value: "3.3.1" }, + { label: "3.2.6 +", value: "3.2.6" }, + { label: "2.2.1 +", value: "2.2.1" }, + { label: "2.2.0 +", value: "2.2.0" }, + { label: "2.1.2 +", value: "2.1.2" }, + { label: "2.1.1 +", value: "2.1.1" }, + { label: "2.0.0 +", value: "2.0.0" }, + { label: "1.8.2 +", value: "1.8.2" }, + { label: "1.8.1 +", value: "1.8.1" }, ]; export class ConfigurePackQueryForm extends Component { @@ -67,11 +77,11 @@ export class ConfigurePackQueryForm extends Component { onCancel: PropTypes.func, }; - componentWillMount () { + componentWillMount() { const { fields } = this.props; if (fields && fields.shard && !fields.shard.value) { - fields.shard.value = ''; + fields.shard.value = ""; } } @@ -81,24 +91,26 @@ export class ConfigurePackQueryForm extends Component { const { formData, onCancel: handleCancel } = this.props; return handleCancel(formData); - } + }; handlePlatformChoice = (value) => { - const { fields: { platform } } = this.props; - const valArray = value.split(','); + const { + fields: { platform }, + } = this.props; + const valArray = value.split(","); // Remove All if another OS is chosen - if (valArray.indexOf('') === 0 && valArray.length > 1) { - return platform.onChange(pull(valArray, '').join(',')); + if (valArray.indexOf("") === 0 && valArray.length > 1) { + return platform.onChange(pull(valArray, "").join(",")); } // Remove OS if All is chosen - if (valArray.length > 1 && valArray.indexOf('') > -1) { - return platform.onChange(''); + if (valArray.length > 1 && valArray.indexOf("") > -1) { + return platform.onChange(""); } return platform.onChange(value); - } + }; renderCancelButton = () => { const { formData } = this.props; @@ -109,13 +121,17 @@ export class ConfigurePackQueryForm extends Component { } return ( - ); - } + }; - render () { + render() { const { fields, handleSubmit } = this.props; const { handlePlatformChoice, renderCancelButton } = this; @@ -144,7 +160,11 @@ export class ConfigurePackQueryForm extends Component { {...fields.version} options={minOsqueryVersionOptions} placeholder="- - -" - label={['minimum ', , ' version']} + label={[ + "minimum ", + , + " version", + ]} wrapperClassName={`${baseClass}__form-field ${baseClass}__form-field--osquer-vers`} />
    {renderCancelButton()} -
    diff --git a/frontend/components/forms/ConfigurePackQueryForm/ConfigurePackQueryForm.tests.jsx b/frontend/components/forms/ConfigurePackQueryForm/ConfigurePackQueryForm.tests.jsx index a74fe4d6b..8014cba09 100644 --- a/frontend/components/forms/ConfigurePackQueryForm/ConfigurePackQueryForm.tests.jsx +++ b/frontend/components/forms/ConfigurePackQueryForm/ConfigurePackQueryForm.tests.jsx @@ -1,29 +1,30 @@ -import React from 'react'; -import { mount } from 'enzyme'; -import { noop } from 'lodash'; +import React from "react"; +import { mount } from "enzyme"; +import { noop } from "lodash"; -import DefaultConfigurePackQueryForm, { ConfigurePackQueryForm } from 'components/forms/ConfigurePackQueryForm/ConfigurePackQueryForm'; -import { itBehavesLikeAFormDropdownElement, itBehavesLikeAFormInputElement } from 'test/helpers'; -import { scheduledQueryStub } from 'test/stubs'; +import DefaultConfigurePackQueryForm, { + ConfigurePackQueryForm, +} from "components/forms/ConfigurePackQueryForm/ConfigurePackQueryForm"; +import { + itBehavesLikeAFormDropdownElement, + itBehavesLikeAFormInputElement, +} from "test/helpers"; +import { scheduledQueryStub } from "test/stubs"; -describe('ConfigurePackQueryForm - component', () => { - describe('form fields', () => { - const form = mount( - , - ); +describe("ConfigurePackQueryForm - component", () => { + describe("form fields", () => { + const form = mount(); - it('updates form state', () => { - itBehavesLikeAFormInputElement(form, 'interval'); - itBehavesLikeAFormDropdownElement(form, 'logging_type'); - itBehavesLikeAFormDropdownElement(form, 'platform'); - itBehavesLikeAFormDropdownElement(form, 'version'); - itBehavesLikeAFormInputElement(form, 'shard'); + it("updates form state", () => { + itBehavesLikeAFormInputElement(form, "interval"); + itBehavesLikeAFormDropdownElement(form, "logging_type"); + itBehavesLikeAFormDropdownElement(form, "platform"); + itBehavesLikeAFormDropdownElement(form, "version"); + itBehavesLikeAFormInputElement(form, "shard"); }); }); - describe('platform options', () => { + describe("platform options", () => { const onChangeSpy = jest.fn(); const fieldsObj = { platform: { @@ -38,85 +39,86 @@ describe('ConfigurePackQueryForm - component', () => { fields={fieldsObj} handleSubmit={noop} formData={{ query_id: 1 }} - />, + /> ); it("doesn't allow All when other options are chosen", () => { - form.instance().handlePlatformChoice(',windows'); + form.instance().handlePlatformChoice(",windows"); - expect(onChangeSpy).toHaveBeenCalledWith('windows'); + expect(onChangeSpy).toHaveBeenCalledWith("windows"); }); it("doesn't allow other options when All is chosen", () => { - form.instance().handlePlatformChoice('darwin,linux,'); + form.instance().handlePlatformChoice("darwin,linux,"); - expect(onChangeSpy).toHaveBeenCalledWith(''); + expect(onChangeSpy).toHaveBeenCalledWith(""); }); }); - describe('submitting the form', () => { + describe("submitting the form", () => { const spy = jest.fn(); const form = mount( , + /> ); - it('submits the form with the form data', () => { - itBehavesLikeAFormInputElement(form, 'interval', 'InputField', 123); - itBehavesLikeAFormDropdownElement(form, 'logging_type'); - itBehavesLikeAFormDropdownElement(form, 'platform'); - itBehavesLikeAFormDropdownElement(form, 'version'); - itBehavesLikeAFormInputElement(form, 'shard', 'InputField', 12); + it("submits the form with the form data", () => { + itBehavesLikeAFormInputElement(form, "interval", "InputField", 123); + itBehavesLikeAFormDropdownElement(form, "logging_type"); + itBehavesLikeAFormDropdownElement(form, "platform"); + itBehavesLikeAFormDropdownElement(form, "version"); + itBehavesLikeAFormInputElement(form, "shard", "InputField", 12); - form.find('form').simulate('submit'); + form.find("form").simulate("submit"); expect(spy).toHaveBeenCalledWith({ interval: 123, - logging_type: 'differential', - platform: '', + logging_type: "differential", + platform: "", query_id: 1, - version: '', + version: "", shard: 12, }); }); }); - describe('cancelling the form', () => { - const CancelButton = form => form.find('.configure-pack-query-form__cancel-btn'); + describe("cancelling the form", () => { + const CancelButton = (form) => + form.find(".configure-pack-query-form__cancel-btn"); - it('displays a cancel Button when updating a scheduled query', () => { + it("displays a cancel Button when updating a scheduled query", () => { const NewScheduledQueryForm = mount( , + /> ); const UpdateScheduledQueryForm = mount( , + /> ); expect(CancelButton(NewScheduledQueryForm).length).toEqual(0); expect(CancelButton(UpdateScheduledQueryForm).length).toBeGreaterThan(0); }); - it('calls the onCancel prop when the cancel Button is clicked', () => { + it("calls the onCancel prop when the cancel Button is clicked", () => { const spy = jest.fn(); const UpdateScheduledQueryForm = mount( , + /> ); - CancelButton(UpdateScheduledQueryForm).hostNodes().simulate('click'); + CancelButton(UpdateScheduledQueryForm).hostNodes().simulate("click"); expect(spy).toHaveBeenCalledWith(scheduledQueryStub); }); diff --git a/frontend/components/forms/ConfigurePackQueryForm/_styles.scss b/frontend/components/forms/ConfigurePackQueryForm/_styles.scss index cc4ea94d6..1101fd1d6 100644 --- a/frontend/components/forms/ConfigurePackQueryForm/_styles.scss +++ b/frontend/components/forms/ConfigurePackQueryForm/_styles.scss @@ -1,5 +1,4 @@ .configure-pack-query-form { - &__form-field { &--interval { position: relative; diff --git a/frontend/components/forms/ConfigurePackQueryForm/index.js b/frontend/components/forms/ConfigurePackQueryForm/index.js index 1fc029d88..097a5d194 100644 --- a/frontend/components/forms/ConfigurePackQueryForm/index.js +++ b/frontend/components/forms/ConfigurePackQueryForm/index.js @@ -1 +1 @@ -export { default } from './ConfigurePackQueryForm'; +export { default } from "./ConfigurePackQueryForm"; diff --git a/frontend/components/forms/ConfigurePackQueryForm/validate.js b/frontend/components/forms/ConfigurePackQueryForm/validate.js index b7c6179f2..d66145863 100644 --- a/frontend/components/forms/ConfigurePackQueryForm/validate.js +++ b/frontend/components/forms/ConfigurePackQueryForm/validate.js @@ -1,29 +1,29 @@ -import { size } from 'lodash'; +import { size } from "lodash"; -import validateNumericality from 'components/forms/validators/validate_numericality'; +import validateNumericality from "components/forms/validators/validate_numericality"; const validate = (formData) => { const errors = {}; if (!formData.query_id) { - errors.query_id = 'A query must be selected'; + errors.query_id = "A query must be selected"; } if (!formData.interval) { - errors.interval = 'Interval must be present'; + errors.interval = "Interval must be present"; } if (formData.interval && !validateNumericality(formData.interval)) { - errors.interval = 'Interval must be a number'; + errors.interval = "Interval must be a number"; } if (!formData.logging_type) { - errors.logging_type = 'A Logging Type must be selected'; + errors.logging_type = "A Logging Type must be selected"; } if (formData.shard) { if (formData.shard < 0 || formData.shard > 100) { - errors.shard = 'Shard must be between 0 and 100'; + errors.shard = "Shard must be between 0 and 100"; } } diff --git a/frontend/components/forms/ConfirmInviteForm/ConfirmInviteForm.jsx b/frontend/components/forms/ConfirmInviteForm/ConfirmInviteForm.jsx index 8ea6b2d4a..54c9f000e 100644 --- a/frontend/components/forms/ConfirmInviteForm/ConfirmInviteForm.jsx +++ b/frontend/components/forms/ConfirmInviteForm/ConfirmInviteForm.jsx @@ -1,13 +1,13 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; +import React, { Component } from "react"; +import PropTypes from "prop-types"; -import Form from 'components/forms/Form'; -import formFieldInterface from 'interfaces/form_field'; -import Button from 'components/buttons/Button'; -import InputFieldWithIcon from 'components/forms/fields/InputFieldWithIcon'; -import helpers from './helpers'; +import Form from "components/forms/Form"; +import formFieldInterface from "interfaces/form_field"; +import Button from "components/buttons/Button"; +import InputFieldWithIcon from "components/forms/fields/InputFieldWithIcon"; +import helpers from "./helpers"; -const formFields = ['name', 'username', 'password', 'password_confirmation']; +const formFields = ["name", "username", "password", "password_confirmation"]; const { validate } = helpers; class ConfirmInviteForm extends Component { @@ -23,7 +23,7 @@ class ConfirmInviteForm extends Component { handleSubmit: PropTypes.func.isRequired, }; - render () { + render() { const { baseError, className, fields, handleSubmit } = this.props; return ( @@ -35,15 +35,14 @@ class ConfirmInviteForm extends Component { autofocus placeholder="Full Name" /> - +
    -
    @@ -65,4 +68,3 @@ export default Form(ConfirmInviteForm, { fields: formFields, validate, }); - diff --git a/frontend/components/forms/ConfirmInviteForm/ConfirmInviteForm.tests.jsx b/frontend/components/forms/ConfirmInviteForm/ConfirmInviteForm.tests.jsx index 6f82070a5..6e68dc631 100644 --- a/frontend/components/forms/ConfirmInviteForm/ConfirmInviteForm.tests.jsx +++ b/frontend/components/forms/ConfirmInviteForm/ConfirmInviteForm.tests.jsx @@ -1,119 +1,137 @@ -import React from 'react'; -import { mount } from 'enzyme'; -import { noop } from 'lodash'; +import React from "react"; +import { mount } from "enzyme"; +import { noop } from "lodash"; -import ConfirmInviteForm from 'components/forms/ConfirmInviteForm'; -import { fillInFormInput } from 'test/helpers'; +import ConfirmInviteForm from "components/forms/ConfirmInviteForm"; +import { fillInFormInput } from "test/helpers"; -describe('ConfirmInviteForm - component', () => { +describe("ConfirmInviteForm - component", () => { const handleSubmitSpy = jest.fn(); - const inviteToken = 'abc123'; + const inviteToken = "abc123"; const formData = { invite_token: inviteToken }; - const form = mount(); + const form = mount( + + ); - const nameInput = form.find({ name: 'name' }).find('input'); - const passwordConfirmationInput = form.find({ name: 'password_confirmation' }).find('input'); - const passwordInput = form.find({ name: 'password' }).find('input'); - const submitBtn = form.find('button'); - const usernameInput = form.find({ name: 'username' }).find('input'); + const nameInput = form.find({ name: "name" }).find("input"); + const passwordConfirmationInput = form + .find({ name: "password_confirmation" }) + .find("input"); + const passwordInput = form.find({ name: "password" }).find("input"); + const submitBtn = form.find("button"); + const usernameInput = form.find({ name: "username" }).find("input"); - it('renders', () => { + it("renders", () => { expect(form.length).toEqual(1); }); - it('renders the base error', () => { - const baseError = 'Unable to authenticate the current user'; - const formWithError = mount(); + it("renders the base error", () => { + const baseError = "Unable to authenticate the current user"; + const formWithError = mount( + + ); const formWithoutError = mount(); expect(formWithError.text()).toContain(baseError); expect(formWithoutError.text()).not.toContain(baseError); }); - it('calls the handleSubmit prop with the invite_token when valid', () => { - fillInFormInput(nameInput, 'Gnar Dog'); - fillInFormInput(usernameInput, 'gnardog'); - fillInFormInput(passwordInput, 'p@ssw0rd'); - fillInFormInput(passwordConfirmationInput, 'p@ssw0rd'); - submitBtn.simulate('click'); + it("calls the handleSubmit prop with the invite_token when valid", () => { + fillInFormInput(nameInput, "Gnar Dog"); + fillInFormInput(usernameInput, "gnardog"); + fillInFormInput(passwordInput, "p@ssw0rd"); + fillInFormInput(passwordConfirmationInput, "p@ssw0rd"); + submitBtn.simulate("click"); expect(handleSubmitSpy).toHaveBeenCalledWith({ ...formData, - name: 'Gnar Dog', - username: 'gnardog', - password: 'p@ssw0rd', - password_confirmation: 'p@ssw0rd', + name: "Gnar Dog", + username: "gnardog", + password: "p@ssw0rd", + password_confirmation: "p@ssw0rd", }); }); - describe('name input', () => { - it('changes form state on change', () => { - fillInFormInput(nameInput, 'Gnar Dog'); + describe("name input", () => { + it("changes form state on change", () => { + fillInFormInput(nameInput, "Gnar Dog"); - expect(form.state().formData).toMatchObject({ name: 'Gnar Dog' }); + expect(form.state().formData).toMatchObject({ name: "Gnar Dog" }); }); - it('validates the field must be present', () => { - fillInFormInput(nameInput, ''); - form.find('button').simulate('click'); - - expect(form.state().errors).toMatchObject({ name: 'Full name must be present' }); - }); - }); - - describe('username input', () => { - it('changes form state on change', () => { - fillInFormInput(usernameInput, 'gnardog'); - - expect(form.state().formData).toMatchObject({ username: 'gnardog' }); - }); - - it('validates the field must be present', () => { - fillInFormInput(usernameInput, ''); - submitBtn.simulate('click'); - - expect(form.state().errors).toMatchObject({ username: 'Username must be present' }); - }); - }); - - describe('password input', () => { - it('changes form state on change', () => { - fillInFormInput(passwordInput, 'p@ssw0rd'); - - expect(form.state().formData).toMatchObject({ password: 'p@ssw0rd' }); - }); - - it('validates the field must be present', () => { - fillInFormInput(passwordInput, ''); - form.find('button').simulate('click'); - - expect(form.state().errors).toMatchObject({ password: 'Password must be present' }); - }); - }); - - describe('password_confirmation input', () => { - it('changes form state on change', () => { - fillInFormInput(passwordConfirmationInput, 'p@ssw0rd'); - - expect(form.state().formData).toMatchObject({ password_confirmation: 'p@ssw0rd' }); - }); - - it('validates the password_confirmation matches the password', () => { - fillInFormInput(passwordInput, 'p@ssw0rd'); - fillInFormInput(passwordConfirmationInput, 'another-password'); - form.find('button').simulate('click'); + it("validates the field must be present", () => { + fillInFormInput(nameInput, ""); + form.find("button").simulate("click"); expect(form.state().errors).toMatchObject({ - password_confirmation: 'Password confirmation does not match password', + name: "Full name must be present", + }); + }); + }); + + describe("username input", () => { + it("changes form state on change", () => { + fillInFormInput(usernameInput, "gnardog"); + + expect(form.state().formData).toMatchObject({ username: "gnardog" }); + }); + + it("validates the field must be present", () => { + fillInFormInput(usernameInput, ""); + submitBtn.simulate("click"); + + expect(form.state().errors).toMatchObject({ + username: "Username must be present", + }); + }); + }); + + describe("password input", () => { + it("changes form state on change", () => { + fillInFormInput(passwordInput, "p@ssw0rd"); + + expect(form.state().formData).toMatchObject({ password: "p@ssw0rd" }); + }); + + it("validates the field must be present", () => { + fillInFormInput(passwordInput, ""); + form.find("button").simulate("click"); + + expect(form.state().errors).toMatchObject({ + password: "Password must be present", + }); + }); + }); + + describe("password_confirmation input", () => { + it("changes form state on change", () => { + fillInFormInput(passwordConfirmationInput, "p@ssw0rd"); + + expect(form.state().formData).toMatchObject({ + password_confirmation: "p@ssw0rd", }); }); - it('validates the field must be present', () => { - fillInFormInput(passwordConfirmationInput, ''); - form.find('button').simulate('click'); + it("validates the password_confirmation matches the password", () => { + fillInFormInput(passwordInput, "p@ssw0rd"); + fillInFormInput(passwordConfirmationInput, "another-password"); + form.find("button").simulate("click"); - expect(form.state().errors).toMatchObject({ password_confirmation: 'Password confirmation must be present' }); + expect(form.state().errors).toMatchObject({ + password_confirmation: "Password confirmation does not match password", + }); + }); + + it("validates the field must be present", () => { + fillInFormInput(passwordConfirmationInput, ""); + form.find("button").simulate("click"); + + expect(form.state().errors).toMatchObject({ + password_confirmation: "Password confirmation must be present", + }); }); }); }); - diff --git a/frontend/components/forms/ConfirmInviteForm/helpers.js b/frontend/components/forms/ConfirmInviteForm/helpers.js index ec24ee44d..91304cf35 100644 --- a/frontend/components/forms/ConfirmInviteForm/helpers.js +++ b/frontend/components/forms/ConfirmInviteForm/helpers.js @@ -1,5 +1,5 @@ -import { size } from 'lodash'; -import validateEquality from 'components/forms/validators/validate_equality'; +import { size } from "lodash"; +import validateEquality from "components/forms/validators/validate_equality"; const validate = (formData) => { const errors = {}; @@ -11,23 +11,28 @@ const validate = (formData) => { } = formData; if (!name) { - errors.name = 'Full name must be present'; + errors.name = "Full name must be present"; } if (!username) { - errors.username = 'Username must be present'; + errors.username = "Username must be present"; } - if (password && passwordConfirmation && !validateEquality(password, passwordConfirmation)) { - errors.password_confirmation = 'Password confirmation does not match password'; + if ( + password && + passwordConfirmation && + !validateEquality(password, passwordConfirmation) + ) { + errors.password_confirmation = + "Password confirmation does not match password"; } if (!password) { - errors.password = 'Password must be present'; + errors.password = "Password must be present"; } if (!passwordConfirmation) { - errors.password_confirmation = 'Password confirmation must be present'; + errors.password_confirmation = "Password confirmation must be present"; } const valid = !size(errors); diff --git a/frontend/components/forms/ConfirmInviteForm/index.js b/frontend/components/forms/ConfirmInviteForm/index.js index 155d65e57..1ecd9243c 100644 --- a/frontend/components/forms/ConfirmInviteForm/index.js +++ b/frontend/components/forms/ConfirmInviteForm/index.js @@ -1 +1 @@ -export { default } from './ConfirmInviteForm'; +export { default } from "./ConfirmInviteForm"; diff --git a/frontend/components/forms/ConfirmSSOInviteForm/ConfirmSSOInviteForm.jsx b/frontend/components/forms/ConfirmSSOInviteForm/ConfirmSSOInviteForm.jsx index 0c123bcf8..45b807fdc 100644 --- a/frontend/components/forms/ConfirmSSOInviteForm/ConfirmSSOInviteForm.jsx +++ b/frontend/components/forms/ConfirmSSOInviteForm/ConfirmSSOInviteForm.jsx @@ -1,13 +1,13 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; +import React, { Component } from "react"; +import PropTypes from "prop-types"; -import Form from 'components/forms/Form'; -import formFieldInterface from 'interfaces/form_field'; -import Button from 'components/buttons/Button'; -import InputFieldWithIcon from 'components/forms/fields/InputFieldWithIcon'; -import helpers from './helpers'; +import Form from "components/forms/Form"; +import formFieldInterface from "interfaces/form_field"; +import Button from "components/buttons/Button"; +import InputFieldWithIcon from "components/forms/fields/InputFieldWithIcon"; +import helpers from "./helpers"; -const formFields = ['name', 'username', 'password', 'password_confirmation']; +const formFields = ["name", "username", "password", "password_confirmation"]; const { validate } = helpers; class ConfirmSSOInviteForm extends Component { @@ -23,7 +23,7 @@ class ConfirmSSOInviteForm extends Component { handleSubmit: PropTypes.func.isRequired, }; - render () { + render() { const { baseError, className, fields, handleSubmit } = this.props; return ( @@ -35,10 +35,7 @@ class ConfirmSSOInviteForm extends Component { autofocus placeholder="Full Name" /> - +
    ); - } + }; showSingleSignOnButton = () => { const { ssoSettings, handleSSOSignOn } = this.props; const { idp_name: idpName, idp_image_url: imageURL } = ssoSettings; const { showLegendWithImage } = this; - let legend = 'Single Sign On'; - if (idpName !== '') { + let legend = "Single Sign On"; + if (idpName !== "") { legend = `Sign On With ${idpName}`; } - if (imageURL !== '') { + if (imageURL !== "") { legend = showLegendWithImage(imageURL, idpName); } @@ -61,22 +61,25 @@ class LoginForm extends Component { variant="inverse" onClick={handleSSOSignOn} > -
    - {legend} -
    +
    {legend}
    ); - } + }; - render () { - const { baseError, fields, handleSubmit, isHidden, ssoSettings } = this.props; + render() { + const { + baseError, + fields, + handleSubmit, + isHidden, + ssoSettings, + } = this.props; const { sso_enabled: ssoEnabled } = ssoSettings; const { showSingleSignOnButton } = this; - const loginFormClass = classnames( - baseClass, - { [`${baseClass}--hidden`]: isHidden }, - ); + const loginFormClass = classnames(baseClass, { + [`${baseClass}--hidden`]: isHidden, + }); return ( @@ -93,7 +96,12 @@ class LoginForm extends Component { type="password" />
    - Forgot Password? + + Forgot Password? +
    - { ssoEnabled && showSingleSignOnButton()} + {ssoEnabled && showSingleSignOnButton()} ); diff --git a/frontend/components/forms/LoginForm/LoginForm.tests.jsx b/frontend/components/forms/LoginForm/LoginForm.tests.jsx index f94a00fa5..632fd8c46 100644 --- a/frontend/components/forms/LoginForm/LoginForm.tests.jsx +++ b/frontend/components/forms/LoginForm/LoginForm.tests.jsx @@ -1,81 +1,96 @@ -import React from 'react'; -import { mount } from 'enzyme'; -import { noop } from 'lodash'; -import LoginForm from './LoginForm'; -import { fillInFormInput } from '../../../test/helpers'; +import React from "react"; +import { mount } from "enzyme"; +import { noop } from "lodash"; +import LoginForm from "./LoginForm"; +import { fillInFormInput } from "../../../test/helpers"; -describe('LoginForm - component', () => { +describe("LoginForm - component", () => { const settings = { sso_enabled: false }; - it('renders the base error', () => { - const baseError = 'Unable to authenticate the current user'; - const formWithError = mount(); - const formWithoutError = mount(); + it("renders the base error", () => { + const baseError = "Unable to authenticate the current user"; + const formWithError = mount( + + ); + const formWithoutError = mount( + + ); expect(formWithError.text()).toContain(baseError); expect(formWithoutError.text()).not.toContain(baseError); }); - it('renders 2 InputField components', () => { - const form = mount(); + it("renders 2 InputField components", () => { + const form = mount( + + ); - expect(form.find('InputFieldWithIcon').length).toEqual(2); + expect(form.find("InputFieldWithIcon").length).toEqual(2); }); - it('updates component state when the username field is changed', () => { - const form = mount(); - const username = 'hi@thegnar.co'; + it("updates component state when the username field is changed", () => { + const form = mount( + + ); + const username = "hi@thegnar.co"; - const usernameField = form.find({ name: 'username' }); + const usernameField = form.find({ name: "username" }); fillInFormInput(usernameField, username); const { formData } = form.state(); expect(formData).toMatchObject({ username }); }); - it('updates component state when the password field is changed', () => { - const form = mount(); + it("updates component state when the password field is changed", () => { + const form = mount( + + ); - const passwordField = form.find({ name: 'password' }); - fillInFormInput(passwordField, 'hello'); + const passwordField = form.find({ name: "password" }); + fillInFormInput(passwordField, "hello"); const { formData } = form.state(); expect(formData).toMatchObject({ - password: 'hello', + password: "hello", }); }); - it( - 'it does not submit the form when the form fields have not been filled out', - () => { - const submitSpy = jest.fn(); - const form = mount(); - const submitBtn = form.find('button'); + it("it does not submit the form when the form fields have not been filled out", () => { + const submitSpy = jest.fn(); + const form = mount( + + ); + const submitBtn = form.find("button"); - submitBtn.simulate('click'); + submitBtn.simulate("click"); - expect(form.state().errors).toMatchObject({ - username: 'Username or email field must be completed', - }); - expect(submitSpy).not.toHaveBeenCalled(); - }, - ); + expect(form.state().errors).toMatchObject({ + username: "Username or email field must be completed", + }); + expect(submitSpy).not.toHaveBeenCalled(); + }); - it('submits the form data when form is submitted', () => { + it("submits the form data when form is submitted", () => { const submitSpy = jest.fn(); - const form = mount(); - const usernameField = form.find({ name: 'username' }); - const passwordField = form.find({ name: 'password' }); - const submitBtn = form.find('button'); + const form = mount( + + ); + const usernameField = form.find({ name: "username" }); + const passwordField = form.find({ name: "password" }); + const submitBtn = form.find("button"); - fillInFormInput(usernameField, 'my@email.com'); - fillInFormInput(passwordField, 'p@ssw0rd'); - submitBtn.simulate('submit'); + fillInFormInput(usernameField, "my@email.com"); + fillInFormInput(passwordField, "p@ssw0rd"); + submitBtn.simulate("submit"); expect(submitSpy).toHaveBeenCalledWith({ - username: 'my@email.com', - password: 'p@ssw0rd', + username: "my@email.com", + password: "p@ssw0rd", }); }); }); diff --git a/frontend/components/forms/LoginForm/index.js b/frontend/components/forms/LoginForm/index.js index 1195444e5..8059f00fd 100644 --- a/frontend/components/forms/LoginForm/index.js +++ b/frontend/components/forms/LoginForm/index.js @@ -1 +1 @@ -export { default } from './LoginForm'; +export { default } from "./LoginForm"; diff --git a/frontend/components/forms/LoginForm/validate.js b/frontend/components/forms/LoginForm/validate.js index b7fd14faf..668f87d8b 100644 --- a/frontend/components/forms/LoginForm/validate.js +++ b/frontend/components/forms/LoginForm/validate.js @@ -1,16 +1,16 @@ -import { size } from 'lodash'; -import validatePresence from 'components/forms/validators/validate_presence'; +import { size } from "lodash"; +import validatePresence from "components/forms/validators/validate_presence"; const validate = (formData) => { const errors = {}; const { password, username } = formData; if (!validatePresence(username)) { - errors.username = 'Username or email field must be completed'; + errors.username = "Username or email field must be completed"; } if (!validatePresence(password)) { - errors.password = 'Password field must be completed'; + errors.password = "Password field must be completed"; } const valid = !size(errors); diff --git a/frontend/components/forms/LogoutForm/LogoutForm.jsx b/frontend/components/forms/LogoutForm/LogoutForm.jsx index eb76dd1d8..d0c420cc2 100644 --- a/frontend/components/forms/LogoutForm/LogoutForm.jsx +++ b/frontend/components/forms/LogoutForm/LogoutForm.jsx @@ -1,10 +1,10 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; +import React, { Component } from "react"; +import PropTypes from "prop-types"; -import Button from '../../buttons/Button'; -import userInterface from '../../../interfaces/user'; +import Button from "../../buttons/Button"; +import userInterface from "../../../interfaces/user"; -const baseClass = 'logout-form'; +const baseClass = "logout-form"; class LogoutForm extends Component { static propTypes = { @@ -18,9 +18,9 @@ class LogoutForm extends Component { const { onSubmit } = this.props; return onSubmit(); - } + }; - render () { + render() { const { user } = this.props; const { gravatarURL } = user; const { onFormSubmit } = this; @@ -28,9 +28,15 @@ class LogoutForm extends Component { return (
    - Avatar + Avatar

    {user.username}

    -

    Are you sure you want to log out?

    +

    + Are you sure you want to log out? +

    diff --git a/frontend/components/forms/RegistrationForm/AdminDetails/AdminDetails.tests.jsx b/frontend/components/forms/RegistrationForm/AdminDetails/AdminDetails.tests.jsx index 714974c4c..36d5e7f75 100644 --- a/frontend/components/forms/RegistrationForm/AdminDetails/AdminDetails.tests.jsx +++ b/frontend/components/forms/RegistrationForm/AdminDetails/AdminDetails.tests.jsx @@ -1,116 +1,124 @@ -import React from 'react'; -import { mount } from 'enzyme'; -import { noop } from 'lodash'; +import React from "react"; +import { mount } from "enzyme"; +import { noop } from "lodash"; -import AdminDetails from 'components/forms/RegistrationForm/AdminDetails'; -import { fillInFormInput, itBehavesLikeAFormInputElement } from 'test/helpers'; +import AdminDetails from "components/forms/RegistrationForm/AdminDetails"; +import { fillInFormInput, itBehavesLikeAFormInputElement } from "test/helpers"; -describe('AdminDetails - form', () => { +describe("AdminDetails - form", () => { let form = mount(); - describe('username input', () => { - it('renders an input field', () => { - itBehavesLikeAFormInputElement(form, 'username'); + describe("username input", () => { + it("renders an input field", () => { + itBehavesLikeAFormInputElement(form, "username"); }); }); - describe('password input', () => { - it('renders an input field', () => { - itBehavesLikeAFormInputElement(form, 'password'); + describe("password input", () => { + it("renders an input field", () => { + itBehavesLikeAFormInputElement(form, "password"); }); }); - describe('password confirmation input', () => { - it('renders an input field', () => { - itBehavesLikeAFormInputElement(form, 'password_confirmation'); + describe("password confirmation input", () => { + it("renders an input field", () => { + itBehavesLikeAFormInputElement(form, "password_confirmation"); }); }); - describe('email input', () => { - it('renders an input field', () => { - itBehavesLikeAFormInputElement(form, 'email'); + describe("email input", () => { + it("renders an input field", () => { + itBehavesLikeAFormInputElement(form, "email"); }); }); - describe('submitting the form', () => { - it('validates missing fields', () => { + describe("submitting the form", () => { + it("validates missing fields", () => { const onSubmitSpy = jest.fn(); form = mount(); - const htmlForm = form.find('form'); + const htmlForm = form.find("form"); - - htmlForm.simulate('submit'); + htmlForm.simulate("submit"); expect(onSubmitSpy).not.toHaveBeenCalled(); expect(form.state().errors).toMatchObject({ - email: 'Email must be present', - password: 'Password must be present', - password_confirmation: 'Password confirmation must be present', - username: 'Username must be present', + email: "Email must be present", + password: "Password must be present", + password_confirmation: "Password confirmation must be present", + username: "Username must be present", }); }); - it('validates the email field', () => { + it("validates the email field", () => { const onSubmitSpy = jest.fn(); form = mount(); - const emailField = form.find({ name: 'email' }).find('input'); - const htmlForm = form.find('form'); + const emailField = form.find({ name: "email" }).find("input"); + const htmlForm = form.find("form"); - fillInFormInput(emailField, 'invalid-email'); - htmlForm.simulate('submit'); - - expect(onSubmitSpy).not.toHaveBeenCalled(); - expect(form.state().errors).toMatchObject({ email: 'Email must be a valid email' }); - }); - - it('validates the password fields match', () => { - const onSubmitSpy = jest.fn(); - form = mount(); - const passwordConfirmationField = form.find({ name: 'password_confirmation' }).find('input'); - const passwordField = form.find({ name: 'password' }).find('input'); - const htmlForm = form.find('form'); - - fillInFormInput(passwordField, 'p@ssw0rd'); - fillInFormInput(passwordConfirmationField, 'password123'); - htmlForm.simulate('submit'); + fillInFormInput(emailField, "invalid-email"); + htmlForm.simulate("submit"); expect(onSubmitSpy).not.toHaveBeenCalled(); expect(form.state().errors).toMatchObject({ - password_confirmation: 'Password confirmation does not match password', + email: "Email must be a valid email", }); }); - it('validates the password field', () => { + it("validates the password fields match", () => { const onSubmitSpy = jest.fn(); form = mount(); - const passwordConfirmationField = form.find({ name: 'password_confirmation' }).find('input'); - const passwordField = form.find({ name: 'password' }).find('input'); - const htmlForm = form.find('form'); + const passwordConfirmationField = form + .find({ name: "password_confirmation" }) + .find("input"); + const passwordField = form.find({ name: "password" }).find("input"); + const htmlForm = form.find("form"); - fillInFormInput(passwordField, 'passw0rd'); - fillInFormInput(passwordConfirmationField, 'passw0rd'); - htmlForm.simulate('submit'); + fillInFormInput(passwordField, "p@ssw0rd"); + fillInFormInput(passwordConfirmationField, "password123"); + htmlForm.simulate("submit"); expect(onSubmitSpy).not.toHaveBeenCalled(); expect(form.state().errors).toMatchObject({ - password: 'Password must be at least 7 characters and contain at least 1 letter, 1 number, and 1 symbol', + password_confirmation: "Password confirmation does not match password", }); }); - it('submits the form when valid', () => { + it("validates the password field", () => { const onSubmitSpy = jest.fn(); form = mount(); - const emailField = form.find({ name: 'email' }).find('input'); - const passwordConfirmationField = form.find({ name: 'password_confirmation' }).find('input'); - const passwordField = form.find({ name: 'password' }).find('input'); - const usernameField = form.find({ name: 'username' }).find('input'); - const htmlForm = form.find('form'); + const passwordConfirmationField = form + .find({ name: "password_confirmation" }) + .find("input"); + const passwordField = form.find({ name: "password" }).find("input"); + const htmlForm = form.find("form"); - fillInFormInput(emailField, 'hi@gnar.dog'); - fillInFormInput(passwordField, 'p@ssw0rd'); - fillInFormInput(passwordConfirmationField, 'p@ssw0rd'); - fillInFormInput(usernameField, 'gnardog'); - htmlForm.simulate('submit'); + fillInFormInput(passwordField, "passw0rd"); + fillInFormInput(passwordConfirmationField, "passw0rd"); + htmlForm.simulate("submit"); + + expect(onSubmitSpy).not.toHaveBeenCalled(); + expect(form.state().errors).toMatchObject({ + password: + "Password must be at least 7 characters and contain at least 1 letter, 1 number, and 1 symbol", + }); + }); + + it("submits the form when valid", () => { + const onSubmitSpy = jest.fn(); + form = mount(); + const emailField = form.find({ name: "email" }).find("input"); + const passwordConfirmationField = form + .find({ name: "password_confirmation" }) + .find("input"); + const passwordField = form.find({ name: "password" }).find("input"); + const usernameField = form.find({ name: "username" }).find("input"); + const htmlForm = form.find("form"); + + fillInFormInput(emailField, "hi@gnar.dog"); + fillInFormInput(passwordField, "p@ssw0rd"); + fillInFormInput(passwordConfirmationField, "p@ssw0rd"); + fillInFormInput(usernameField, "gnardog"); + htmlForm.simulate("submit"); expect(onSubmitSpy).toHaveBeenCalled(); }); diff --git a/frontend/components/forms/RegistrationForm/AdminDetails/helpers.js b/frontend/components/forms/RegistrationForm/AdminDetails/helpers.js index 34777678e..d050cd561 100644 --- a/frontend/components/forms/RegistrationForm/AdminDetails/helpers.js +++ b/frontend/components/forms/RegistrationForm/AdminDetails/helpers.js @@ -1,7 +1,7 @@ -import { size } from 'lodash'; -import validateEquality from 'components/forms/validators/validate_equality'; -import validEmail from 'components/forms/validators/valid_email'; -import validPassword from 'components/forms/validators/valid_password'; +import { size } from "lodash"; +import validateEquality from "components/forms/validators/validate_equality"; +import validEmail from "components/forms/validators/valid_email"; +import validPassword from "components/forms/validators/valid_password"; const validate = (formData) => { const errors = {}; @@ -13,31 +13,37 @@ const validate = (formData) => { } = formData; if (!validEmail(email)) { - errors.email = 'Email must be a valid email'; + errors.email = "Email must be a valid email"; } if (!email) { - errors.email = 'Email must be present'; + errors.email = "Email must be present"; } if (!username) { - errors.username = 'Username must be present'; + errors.username = "Username must be present"; } if (password && passwordConfirmation && !validPassword(password)) { - errors.password = 'Password must be at least 7 characters and contain at least 1 letter, 1 number, and 1 symbol'; + errors.password = + "Password must be at least 7 characters and contain at least 1 letter, 1 number, and 1 symbol"; } - if (password && passwordConfirmation && !validateEquality(password, passwordConfirmation)) { - errors.password_confirmation = 'Password confirmation does not match password'; + if ( + password && + passwordConfirmation && + !validateEquality(password, passwordConfirmation) + ) { + errors.password_confirmation = + "Password confirmation does not match password"; } if (!password) { - errors.password = 'Password must be present'; + errors.password = "Password must be present"; } if (!passwordConfirmation) { - errors.password_confirmation = 'Password confirmation must be present'; + errors.password_confirmation = "Password confirmation must be present"; } const valid = !size(errors); diff --git a/frontend/components/forms/RegistrationForm/AdminDetails/index.js b/frontend/components/forms/RegistrationForm/AdminDetails/index.js index a72de0ac4..278bd87d8 100644 --- a/frontend/components/forms/RegistrationForm/AdminDetails/index.js +++ b/frontend/components/forms/RegistrationForm/AdminDetails/index.js @@ -1 +1 @@ -export { default } from './AdminDetails'; +export { default } from "./AdminDetails"; diff --git a/frontend/components/forms/RegistrationForm/ConfirmationPage/ConfirmationPage.jsx b/frontend/components/forms/RegistrationForm/ConfirmationPage/ConfirmationPage.jsx index 460c8699f..30e1387b8 100644 --- a/frontend/components/forms/RegistrationForm/ConfirmationPage/ConfirmationPage.jsx +++ b/frontend/components/forms/RegistrationForm/ConfirmationPage/ConfirmationPage.jsx @@ -1,12 +1,12 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import classnames from 'classnames'; +import React, { Component } from "react"; +import PropTypes from "prop-types"; +import classnames from "classnames"; -import Button from 'components/buttons/Button'; -import formDataInterface from 'interfaces/registration_form_data'; -import Checkbox from 'components/forms/fields/Checkbox'; +import Button from "components/buttons/Button"; +import formDataInterface from "interfaces/registration_form_data"; +import Checkbox from "components/forms/fields/Checkbox"; -const baseClass = 'confirm-user-reg'; +const baseClass = "confirm-user-reg"; class ConfirmationPage extends Component { static propTypes = { @@ -17,7 +17,10 @@ class ConfirmationPage extends Component { }; componentDidUpdate(prevProps) { - if (this.props.currentPage && this.props.currentPage !== prevProps.currentPage) { + if ( + this.props.currentPage && + this.props.currentPage !== prevProps.currentPage + ) { // Component has a transition duration of 300ms set in // RegistrationForm/_styles.scss. We need to wait 300ms before // calling .focus() to preserve smooth transition. @@ -37,14 +40,18 @@ class ConfirmationPage extends Component { return (
    -

    I am migrating an existing osquery installation.

    -

    Take me to the Import Configuration page.

    +

    + I am migrating an existing osquery installation. +

    +

    + Take me to the Import Configuration page. +

    ); - } + }; - render () { + render() { const { importOsqueryConfig } = this; const { className, @@ -81,7 +88,14 @@ class ConfirmationPage extends Component { Fleet URL: - {kolideWebAddress} + + + {kolideWebAddress} + + @@ -89,7 +103,13 @@ class ConfirmationPage extends Component { {importOsqueryConfig()} - diff --git a/frontend/components/forms/RegistrationForm/ConfirmationPage/ConfirmationPage.tests.jsx b/frontend/components/forms/RegistrationForm/ConfirmationPage/ConfirmationPage.tests.jsx index d5b31f9c8..7ea12bba6 100644 --- a/frontend/components/forms/RegistrationForm/ConfirmationPage/ConfirmationPage.tests.jsx +++ b/frontend/components/forms/RegistrationForm/ConfirmationPage/ConfirmationPage.tests.jsx @@ -1,23 +1,20 @@ -import React from 'react'; -import { mount } from 'enzyme'; -import { noop } from 'lodash'; +import React from "react"; +import { mount } from "enzyme"; +import { noop } from "lodash"; -import ConfirmationPage from 'components/forms/RegistrationForm/ConfirmationPage'; +import ConfirmationPage from "components/forms/RegistrationForm/ConfirmationPage"; -describe('ConfirmationPage - form', () => { +describe("ConfirmationPage - form", () => { const formData = { - username: 'jmeller', - email: 'jason@kolide.co', - org_name: 'Kolide', - kolide_server_url: 'http://kolide.kolide.co', + username: "jmeller", + email: "jason@kolide.co", + org_name: "Kolide", + kolide_server_url: "http://kolide.kolide.co", }; - it('renders the user information', () => { + it("renders the user information", () => { const form = mount( - , + ); expect(form.text()).toContain(formData.username); @@ -26,19 +23,15 @@ describe('ConfirmationPage - form', () => { expect(form.text()).toContain(formData.kolide_server_url); }); - it('submits the form', () => { + it("submits the form", () => { const handleSubmitSpy = jest.fn(); const form = mount( - , + ); - const htmlForm = form.find('form'); + const htmlForm = form.find("form"); - htmlForm.simulate('submit'); + htmlForm.simulate("submit"); expect(handleSubmitSpy).toHaveBeenCalled(); }); }); - diff --git a/frontend/components/forms/RegistrationForm/ConfirmationPage/_styles.scss b/frontend/components/forms/RegistrationForm/ConfirmationPage/_styles.scss index e460d4ab1..61328350f 100644 --- a/frontend/components/forms/RegistrationForm/ConfirmationPage/_styles.scss +++ b/frontend/components/forms/RegistrationForm/ConfirmationPage/_styles.scss @@ -56,7 +56,7 @@ &__table-url { @include ellipsis(100%); - font-family: 'SourceCodePro', $monospace; + font-family: "SourceCodePro", $monospace; vertical-align: bottom; font-weight: 600; } diff --git a/frontend/components/forms/RegistrationForm/ConfirmationPage/index.js b/frontend/components/forms/RegistrationForm/ConfirmationPage/index.js index cb3b1b893..7776dd384 100644 --- a/frontend/components/forms/RegistrationForm/ConfirmationPage/index.js +++ b/frontend/components/forms/RegistrationForm/ConfirmationPage/index.js @@ -1 +1 @@ -export { default } from './ConfirmationPage'; +export { default } from "./ConfirmationPage"; diff --git a/frontend/components/forms/RegistrationForm/KolideDetails/KolideDetails.jsx b/frontend/components/forms/RegistrationForm/KolideDetails/KolideDetails.jsx index 13fa99635..c17ac0d57 100644 --- a/frontend/components/forms/RegistrationForm/KolideDetails/KolideDetails.jsx +++ b/frontend/components/forms/RegistrationForm/KolideDetails/KolideDetails.jsx @@ -1,13 +1,13 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; +import React, { Component } from "react"; +import PropTypes from "prop-types"; -import Form from 'components/forms/Form'; -import formFieldInterface from 'interfaces/form_field'; -import Button from 'components/buttons/Button'; -import helpers from 'components/forms/RegistrationForm/KolideDetails/helpers'; -import InputFieldWithIcon from 'components/forms/fields/InputFieldWithIcon'; +import Form from "components/forms/Form"; +import formFieldInterface from "interfaces/form_field"; +import Button from "components/buttons/Button"; +import helpers from "components/forms/RegistrationForm/KolideDetails/helpers"; +import InputFieldWithIcon from "components/forms/fields/InputFieldWithIcon"; -const formFields = ['kolide_server_url']; +const formFields = ["kolide_server_url"]; const { validate } = helpers; class KolideDetails extends Component { @@ -21,7 +21,10 @@ class KolideDetails extends Component { }; componentDidUpdate(prevProps) { - if (this.props.currentPage && this.props.currentPage !== prevProps.currentPage) { + if ( + this.props.currentPage && + this.props.currentPage !== prevProps.currentPage + ) { // Component has a transition duration of 300ms set in // RegistrationForm/_styles.scss. We need to wait 300ms before // calling .focus() to preserve smooth transition. @@ -31,7 +34,7 @@ class KolideDetails extends Component { } } - render () { + render() { const { className, currentPage, fields, handleSubmit } = this.props; const tabIndex = currentPage ? 1 : -1; @@ -42,11 +45,22 @@ class KolideDetails extends Component { {...fields.kolide_server_url} placeholder="Fleet web address" tabIndex={tabIndex} - hint={['Don’t include ', /v1, ' or any other path.']} - ref={(input) => { this.firstInput = input; }} + hint={[ + "Don’t include ", + /v1, + " or any other path.", + ]} + ref={(input) => { + this.firstInput = input; + }} /> - diff --git a/frontend/components/forms/RegistrationForm/KolideDetails/KolideDetails.tests.jsx b/frontend/components/forms/RegistrationForm/KolideDetails/KolideDetails.tests.jsx index 4dcd24283..7a55304f2 100644 --- a/frontend/components/forms/RegistrationForm/KolideDetails/KolideDetails.tests.jsx +++ b/frontend/components/forms/RegistrationForm/KolideDetails/KolideDetails.tests.jsx @@ -1,64 +1,74 @@ -import React from 'react'; -import { mount } from 'enzyme'; -import { noop } from 'lodash'; +import React from "react"; +import { mount } from "enzyme"; +import { noop } from "lodash"; -import KolideDetails from 'components/forms/RegistrationForm/KolideDetails'; -import { fillInFormInput } from 'test/helpers'; +import KolideDetails from "components/forms/RegistrationForm/KolideDetails"; +import { fillInFormInput } from "test/helpers"; -describe('KolideDetails - form', () => { - describe('kolide web address input', () => { - it('renders an input field', () => { +describe("KolideDetails - form", () => { + describe("kolide web address input", () => { + it("renders an input field", () => { const form = mount(); - const kolideWebAddressField = form.find({ name: 'kolide_server_url' }); + const kolideWebAddressField = form.find({ name: "kolide_server_url" }); expect(kolideWebAddressField.length).toBeGreaterThan(0); }); - it('updates state when the field changes', () => { + it("updates state when the field changes", () => { const form = mount(); - const kolideWebAddressField = form.find({ name: 'kolide_server_url' }).find('input'); + const kolideWebAddressField = form + .find({ name: "kolide_server_url" }) + .find("input"); - fillInFormInput(kolideWebAddressField, 'https://gnar.kolide.co'); + fillInFormInput(kolideWebAddressField, "https://gnar.kolide.co"); - expect(form.state().formData).toMatchObject({ kolide_server_url: 'https://gnar.kolide.co' }); + expect(form.state().formData).toMatchObject({ + kolide_server_url: "https://gnar.kolide.co", + }); }); }); - describe('submitting the form', () => { - it('validates the presence of the kolide web address field', () => { + describe("submitting the form", () => { + it("validates the presence of the kolide web address field", () => { const handleSubmitSpy = jest.fn(); const form = mount(); - const htmlForm = form.find('form'); + const htmlForm = form.find("form"); - htmlForm.simulate('submit'); - - expect(handleSubmitSpy).not.toHaveBeenCalled(); - expect(form.state().errors).toMatchObject({ kolide_server_url: 'Kolide web address must be completed' }); - }); - - it('validates the kolide web address field starts with https://', () => { - const handleSubmitSpy = jest.fn(); - const form = mount(); - const kolideWebAddressField = form.find({ name: 'kolide_server_url' }).find('input'); - const htmlForm = form.find('form'); - - fillInFormInput(kolideWebAddressField, 'http://gnar.kolide.co'); - htmlForm.simulate('submit'); + htmlForm.simulate("submit"); expect(handleSubmitSpy).not.toHaveBeenCalled(); expect(form.state().errors).toMatchObject({ - kolide_server_url: 'Kolide web address must start with https://', + kolide_server_url: "Kolide web address must be completed", }); }); - it('submits the form when valid', () => { + it("validates the kolide web address field starts with https://", () => { const handleSubmitSpy = jest.fn(); const form = mount(); - const kolideWebAddressField = form.find({ name: 'kolide_server_url' }).find('input'); - const htmlForm = form.find('form'); + const kolideWebAddressField = form + .find({ name: "kolide_server_url" }) + .find("input"); + const htmlForm = form.find("form"); - fillInFormInput(kolideWebAddressField, 'https://gnar.kolide.co'); - htmlForm.simulate('submit'); + fillInFormInput(kolideWebAddressField, "http://gnar.kolide.co"); + htmlForm.simulate("submit"); + + expect(handleSubmitSpy).not.toHaveBeenCalled(); + expect(form.state().errors).toMatchObject({ + kolide_server_url: "Kolide web address must start with https://", + }); + }); + + it("submits the form when valid", () => { + const handleSubmitSpy = jest.fn(); + const form = mount(); + const kolideWebAddressField = form + .find({ name: "kolide_server_url" }) + .find("input"); + const htmlForm = form.find("form"); + + fillInFormInput(kolideWebAddressField, "https://gnar.kolide.co"); + htmlForm.simulate("submit"); expect(handleSubmitSpy).toHaveBeenCalled(); }); diff --git a/frontend/components/forms/RegistrationForm/KolideDetails/helpers.js b/frontend/components/forms/RegistrationForm/KolideDetails/helpers.js index 10ce62300..d27f8917e 100644 --- a/frontend/components/forms/RegistrationForm/KolideDetails/helpers.js +++ b/frontend/components/forms/RegistrationForm/KolideDetails/helpers.js @@ -1,15 +1,15 @@ -import { size, startsWith } from 'lodash'; +import { size, startsWith } from "lodash"; const validate = (formData) => { const errors = {}; const { kolide_server_url: kolideWebAddress } = formData; if (!kolideWebAddress) { - errors.kolide_server_url = 'Kolide web address must be completed'; + errors.kolide_server_url = "Kolide web address must be completed"; } - if (kolideWebAddress && !startsWith(kolideWebAddress, 'https://')) { - errors.kolide_server_url = 'Kolide web address must start with https://'; + if (kolideWebAddress && !startsWith(kolideWebAddress, "https://")) { + errors.kolide_server_url = "Kolide web address must start with https://"; } const valid = !size(errors); diff --git a/frontend/components/forms/RegistrationForm/KolideDetails/index.js b/frontend/components/forms/RegistrationForm/KolideDetails/index.js index 37a06188b..9334248c0 100644 --- a/frontend/components/forms/RegistrationForm/KolideDetails/index.js +++ b/frontend/components/forms/RegistrationForm/KolideDetails/index.js @@ -1 +1 @@ -export { default } from './KolideDetails'; +export { default } from "./KolideDetails"; diff --git a/frontend/components/forms/RegistrationForm/OrgDetails/OrgDetails.jsx b/frontend/components/forms/RegistrationForm/OrgDetails/OrgDetails.jsx index 212ae57bd..913a858c4 100644 --- a/frontend/components/forms/RegistrationForm/OrgDetails/OrgDetails.jsx +++ b/frontend/components/forms/RegistrationForm/OrgDetails/OrgDetails.jsx @@ -1,13 +1,13 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; +import React, { Component } from "react"; +import PropTypes from "prop-types"; -import Form from 'components/forms/Form'; -import formFieldInterface from 'interfaces/form_field'; -import Button from 'components/buttons/Button'; -import helpers from 'components/forms/RegistrationForm/OrgDetails/helpers'; -import InputFieldWithIcon from 'components/forms/fields/InputFieldWithIcon'; +import Form from "components/forms/Form"; +import formFieldInterface from "interfaces/form_field"; +import Button from "components/buttons/Button"; +import helpers from "components/forms/RegistrationForm/OrgDetails/helpers"; +import InputFieldWithIcon from "components/forms/fields/InputFieldWithIcon"; -const formFields = ['org_name', 'org_logo_url']; +const formFields = ["org_name", "org_logo_url"]; const { validate } = helpers; class OrgDetails extends Component { @@ -22,7 +22,10 @@ class OrgDetails extends Component { }; componentDidUpdate(prevProps) { - if (this.props.currentPage && this.props.currentPage !== prevProps.currentPage) { + if ( + this.props.currentPage && + this.props.currentPage !== prevProps.currentPage + ) { // Component has a transition duration of 300ms set in // RegistrationForm/_styles.scss. We need to wait 300ms before // calling .focus() to preserve smooth transition. @@ -32,7 +35,7 @@ class OrgDetails extends Component { } } - render () { + render() { const { className, currentPage, fields, handleSubmit } = this.props; const tabIndex = currentPage ? 1 : -1; @@ -43,7 +46,9 @@ class OrgDetails extends Component { {...fields.org_name} placeholder="Organization name" tabIndex={tabIndex} - ref={(input) => { this.firstInput = input; }} + ref={(input) => { + this.firstInput = input; + }} /> - diff --git a/frontend/components/forms/RegistrationForm/OrgDetails/OrgDetails.tests.jsx b/frontend/components/forms/RegistrationForm/OrgDetails/OrgDetails.tests.jsx index b0c92e7f5..07cd48135 100644 --- a/frontend/components/forms/RegistrationForm/OrgDetails/OrgDetails.tests.jsx +++ b/frontend/components/forms/RegistrationForm/OrgDetails/OrgDetails.tests.jsx @@ -1,87 +1,89 @@ -import React from 'react'; -import { mount } from 'enzyme'; -import { noop } from 'lodash'; +import React from "react"; +import { mount } from "enzyme"; +import { noop } from "lodash"; -import OrgDetails from 'components/forms/RegistrationForm/OrgDetails'; -import { fillInFormInput } from 'test/helpers'; +import OrgDetails from "components/forms/RegistrationForm/OrgDetails"; +import { fillInFormInput } from "test/helpers"; -describe('OrgDetails - form', () => { - describe('organization name input', () => { - it('renders an input field', () => { +describe("OrgDetails - form", () => { + describe("organization name input", () => { + it("renders an input field", () => { const form = mount(); - const orgNameField = form.find({ name: 'org_name' }); + const orgNameField = form.find({ name: "org_name" }); expect(orgNameField.length).toBeGreaterThan(0); }); - it('updates state when the field changes', () => { + it("updates state when the field changes", () => { const form = mount(); - const orgNameField = form.find({ name: 'org_name' }).find('input'); + const orgNameField = form.find({ name: "org_name" }).find("input"); - fillInFormInput(orgNameField, 'The Gnar Co'); + fillInFormInput(orgNameField, "The Gnar Co"); - expect(form.state().formData).toMatchObject({ org_name: 'The Gnar Co' }); + expect(form.state().formData).toMatchObject({ org_name: "The Gnar Co" }); }); }); - describe('organization logo URL input', () => { - it('renders an input field', () => { + describe("organization logo URL input", () => { + it("renders an input field", () => { const form = mount(); - const orgLogoField = form.find({ name: 'org_logo_url' }); + const orgLogoField = form.find({ name: "org_logo_url" }); expect(orgLogoField.length).toBeGreaterThan(0); }); - it('updates state when the field changes', () => { + it("updates state when the field changes", () => { const form = mount(); - const orgLogoField = form.find({ name: 'org_logo_url' }).find('input'); + const orgLogoField = form.find({ name: "org_logo_url" }).find("input"); - fillInFormInput(orgLogoField, 'http://www.thegnar.co/logo.png'); + fillInFormInput(orgLogoField, "http://www.thegnar.co/logo.png"); - expect(form.state().formData).toMatchObject({ org_logo_url: 'http://www.thegnar.co/logo.png' }); + expect(form.state().formData).toMatchObject({ + org_logo_url: "http://www.thegnar.co/logo.png", + }); }); }); - describe('submitting the form', () => { - it('validates presence of org_name field', () => { + describe("submitting the form", () => { + it("validates presence of org_name field", () => { const handleSubmitSpy = jest.fn(); const form = mount(); - const htmlForm = form.find('form'); + const htmlForm = form.find("form"); - htmlForm.simulate('submit'); + htmlForm.simulate("submit"); expect(handleSubmitSpy).not.toHaveBeenCalled(); expect(form.state().errors).toMatchObject({ - org_name: 'Organization name must be present', + org_name: "Organization name must be present", }); }); - it('validates the logo url field starts with https://', () => { + it("validates the logo url field starts with https://", () => { const handleSubmitSpy = jest.fn(); const form = mount(); - const orgLogoField = form.find({ name: 'org_logo_url' }).find('input'); - const htmlForm = form.find('form'); + const orgLogoField = form.find({ name: "org_logo_url" }).find("input"); + const htmlForm = form.find("form"); - fillInFormInput(orgLogoField, 'http://www.thegnar.co/logo.png'); - htmlForm.simulate('submit'); + fillInFormInput(orgLogoField, "http://www.thegnar.co/logo.png"); + htmlForm.simulate("submit"); expect(handleSubmitSpy).not.toHaveBeenCalled(); expect(form.state().errors).toMatchObject({ - org_logo_url: 'Organization logo URL must start with https://', + org_logo_url: "Organization logo URL must start with https://", }); }); - it('submits the form when valid', () => { + it("submits the form when valid", () => { const handleSubmitSpy = jest.fn(); const form = mount(); - const orgLogoField = form.find({ name: 'org_logo_url' }).find('input'); - const orgNameField = form.find({ name: 'org_name' }).find('input'); - const htmlForm = form.find('form'); + const orgLogoField = form.find({ name: "org_logo_url" }).find("input"); + const orgNameField = form.find({ name: "org_name" }).find("input"); + const htmlForm = form.find("form"); - fillInFormInput(orgLogoField, 'https://www.thegnar.co/logo.png'); - fillInFormInput(orgNameField, 'The Gnar Co'); + fillInFormInput(orgLogoField, "https://www.thegnar.co/logo.png"); + fillInFormInput(orgNameField, "The Gnar Co"); - htmlForm.simulate('submit'); + htmlForm.simulate("submit"); expect(handleSubmitSpy).toHaveBeenCalled(); }); diff --git a/frontend/components/forms/RegistrationForm/OrgDetails/helpers.js b/frontend/components/forms/RegistrationForm/OrgDetails/helpers.js index 4e8d37a2d..bc4ce765e 100644 --- a/frontend/components/forms/RegistrationForm/OrgDetails/helpers.js +++ b/frontend/components/forms/RegistrationForm/OrgDetails/helpers.js @@ -1,18 +1,15 @@ -import { size, startsWith } from 'lodash'; +import { size, startsWith } from "lodash"; const validate = (formData) => { const errors = {}; - const { - org_name: orgName, - org_logo_url: orgLogoUrl, - } = formData; + const { org_name: orgName, org_logo_url: orgLogoUrl } = formData; if (!orgName) { - errors.org_name = 'Organization name must be present'; + errors.org_name = "Organization name must be present"; } - if (orgLogoUrl && !startsWith(orgLogoUrl, 'https://')) { - errors.org_logo_url = 'Organization logo URL must start with https://'; + if (orgLogoUrl && !startsWith(orgLogoUrl, "https://")) { + errors.org_logo_url = "Organization logo URL must start with https://"; } const valid = !size(errors); diff --git a/frontend/components/forms/RegistrationForm/OrgDetails/index.js b/frontend/components/forms/RegistrationForm/OrgDetails/index.js index 93eae8276..8d1b85b2e 100644 --- a/frontend/components/forms/RegistrationForm/OrgDetails/index.js +++ b/frontend/components/forms/RegistrationForm/OrgDetails/index.js @@ -1 +1 @@ -export { default } from './OrgDetails'; +export { default } from "./OrgDetails"; diff --git a/frontend/components/forms/RegistrationForm/RegistrationForm.jsx b/frontend/components/forms/RegistrationForm/RegistrationForm.jsx index 4ce08bc4b..b0a51e5d4 100644 --- a/frontend/components/forms/RegistrationForm/RegistrationForm.jsx +++ b/frontend/components/forms/RegistrationForm/RegistrationForm.jsx @@ -1,13 +1,13 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import classnames from 'classnames'; +import React, { Component } from "react"; +import PropTypes from "prop-types"; +import classnames from "classnames"; -import AdminDetails from 'components/forms/RegistrationForm/AdminDetails'; -import ConfirmationPage from 'components/forms/RegistrationForm/ConfirmationPage'; -import KolideDetails from 'components/forms/RegistrationForm/KolideDetails'; -import OrgDetails from 'components/forms/RegistrationForm/OrgDetails'; +import AdminDetails from "components/forms/RegistrationForm/AdminDetails"; +import ConfirmationPage from "components/forms/RegistrationForm/ConfirmationPage"; +import KolideDetails from "components/forms/RegistrationForm/KolideDetails"; +import OrgDetails from "components/forms/RegistrationForm/OrgDetails"; -const baseClass = 'user-registration'; +const baseClass = "user-registration"; class RegistrationForm extends Component { static propTypes = { @@ -16,7 +16,7 @@ class RegistrationForm extends Component { page: PropTypes.number, }; - constructor (props) { + constructor(props) { super(props); const { window } = global; @@ -40,7 +40,7 @@ class RegistrationForm extends Component { }); return onNextPage(); - } + }; onSubmitConfirmation = (evt) => { evt.preventDefault(); @@ -49,7 +49,7 @@ class RegistrationForm extends Component { const { onSubmit: handleSubmit } = this.props; return handleSubmit(formData); - } + }; isCurrentPage = (num) => { const { page } = this.props; @@ -59,65 +59,62 @@ class RegistrationForm extends Component { } return false; - } + }; - render () { + render() { const { page } = this.props; const { formData } = this.state; const { isCurrentPage, onPageFormSubmit, onSubmitConfirmation } = this; const adminDetailsContainerClass = classnames( `${baseClass}__container`, - `${baseClass}__container--admin`, + `${baseClass}__container--admin` ); const adminDetailsClass = classnames( `${baseClass}__field-wrapper`, - `${baseClass}__field-wrapper--admin`, + `${baseClass}__field-wrapper--admin` ); const orgDetailsContainerClass = classnames( `${baseClass}__container`, - `${baseClass}__container--org`, + `${baseClass}__container--org` ); const orgDetailsClass = classnames( `${baseClass}__field-wrapper`, - `${baseClass}__field-wrapper--org`, + `${baseClass}__field-wrapper--org` ); const kolideDetailsContainerClass = classnames( `${baseClass}__container`, - `${baseClass}__container--kolide`, + `${baseClass}__container--kolide` ); const kolideDetailsClass = classnames( `${baseClass}__field-wrapper`, - `${baseClass}__field-wrapper--kolide`, + `${baseClass}__field-wrapper--kolide` ); const confirmationContainerClass = classnames( `${baseClass}__container`, - `${baseClass}__container--confirmation`, + `${baseClass}__container--confirmation` ); const confirmationClass = classnames( `${baseClass}__field-wrapper`, - `${baseClass}__field-wrapper--confirmation`, + `${baseClass}__field-wrapper--confirmation` ); - const formSectionClasses = classnames( - `${baseClass}__form`, - { - [`${baseClass}__form--step1-active`]: page === 1, - [`${baseClass}__form--step1-complete`]: page > 1, - [`${baseClass}__form--step2-active`]: page === 2, - [`${baseClass}__form--step2-complete`]: page > 2, - [`${baseClass}__form--step3-active`]: page === 3, - [`${baseClass}__form--step3-complete`]: page > 3, - [`${baseClass}__form--step4-active`]: page === 4, - }, - ); + const formSectionClasses = classnames(`${baseClass}__form`, { + [`${baseClass}__form--step1-active`]: page === 1, + [`${baseClass}__form--step1-complete`]: page > 1, + [`${baseClass}__form--step2-active`]: page === 2, + [`${baseClass}__form--step2-complete`]: page > 2, + [`${baseClass}__form--step3-active`]: page === 3, + [`${baseClass}__form--step3-complete`]: page > 3, + [`${baseClass}__form--step4-active`]: page === 4, + }); return (
    @@ -125,19 +122,39 @@ class RegistrationForm extends Component {

    Setup user

    Additional admins can be designated within the Fleet app.

    - +

    Organization details

    - +

    Set Fleet URL

    - +

    You're all set.

    - +
    diff --git a/frontend/components/forms/RegistrationForm/RegistrationForm.tests.jsx b/frontend/components/forms/RegistrationForm/RegistrationForm.tests.jsx index b77ee6126..f72eefbf1 100644 --- a/frontend/components/forms/RegistrationForm/RegistrationForm.tests.jsx +++ b/frontend/components/forms/RegistrationForm/RegistrationForm.tests.jsx @@ -1,35 +1,34 @@ -import React from 'react'; -import { mount } from 'enzyme'; +import React from "react"; +import { mount } from "enzyme"; -import RegistrationForm from 'components/forms/RegistrationForm'; +import RegistrationForm from "components/forms/RegistrationForm"; -describe('RegistrationForm - component', () => { - it('renders AdminDetails and header on the first page', () => { +describe("RegistrationForm - component", () => { + it("renders AdminDetails and header on the first page", () => { const form = mount(); - expect(form.find('AdminDetails').length).toEqual(1); - expect(form.text()).toContain('Setup user'); + expect(form.find("AdminDetails").length).toEqual(1); + expect(form.text()).toContain("Setup user"); }); - it('renders OrgDetails on the second page', () => { + it("renders OrgDetails on the second page", () => { const form = mount(); - expect(form.find('OrgDetails').length).toEqual(1); - expect(form.text()).toContain('Organization details'); + expect(form.find("OrgDetails").length).toEqual(1); + expect(form.text()).toContain("Organization details"); }); - it('renders KolideDetails on the third page', () => { + it("renders KolideDetails on the third page", () => { const form = mount(); - expect(form.find('KolideDetails').length).toEqual(1); - expect(form.text()).toContain('Set Fleet URL'); + expect(form.find("KolideDetails").length).toEqual(1); + expect(form.text()).toContain("Set Fleet URL"); }); - it('renders ConfirmationPage on the fourth page', () => { + it("renders ConfirmationPage on the fourth page", () => { const form = mount(); - expect(form.find('ConfirmationPage').length).toEqual(1); - expect(form.text()).toContain('You\'re all set'); + expect(form.find("ConfirmationPage").length).toEqual(1); + expect(form.text()).toContain("You're all set"); }); }); - diff --git a/frontend/components/forms/RegistrationForm/_styles.scss b/frontend/components/forms/RegistrationForm/_styles.scss index 08ebc057f..37e4e8a20 100644 --- a/frontend/components/forms/RegistrationForm/_styles.scss +++ b/frontend/components/forms/RegistrationForm/_styles.scss @@ -33,25 +33,25 @@ &--admin { left: 0; - top: unquote('max(56%, 480px)'); + top: unquote("max(56%, 480px)"); margin: auto; } &--org { left: calc(100% + 220px); - top: unquote('max(56%, 480px)'); + top: unquote("max(56%, 480px)"); opacity: 0; } &--kolide { left: calc(150% + 220px); - top: unquote('max(56%, 480px)'); + top: unquote("max(56%, 480px)"); opacity: 0; } &--confirmation { left: calc(200% + 220px); - top: unquote('max(56%, 480px)'); + top: unquote("max(56%, 480px)"); opacity: 0; } @@ -100,7 +100,6 @@ } &--step2-complete { - .user-registration__container--admin { left: calc(-50% - 600px); opacity: 0; @@ -124,7 +123,6 @@ } &--step3-complete { - .user-registration__container--admin { left: calc(-100% - 600px); opacity: 0; @@ -191,5 +189,4 @@ width: 100%; } } - } diff --git a/frontend/components/forms/RegistrationForm/index.js b/frontend/components/forms/RegistrationForm/index.js index d04a57865..473e9dbb2 100644 --- a/frontend/components/forms/RegistrationForm/index.js +++ b/frontend/components/forms/RegistrationForm/index.js @@ -1 +1 @@ -export { default } from './RegistrationForm'; +export { default } from "./RegistrationForm"; diff --git a/frontend/components/forms/ResetPasswordForm/ResetPasswordForm.jsx b/frontend/components/forms/ResetPasswordForm/ResetPasswordForm.jsx index 5ada26787..ee698ddc1 100644 --- a/frontend/components/forms/ResetPasswordForm/ResetPasswordForm.jsx +++ b/frontend/components/forms/ResetPasswordForm/ResetPasswordForm.jsx @@ -1,14 +1,14 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; +import React, { Component } from "react"; +import PropTypes from "prop-types"; -import Button from 'components/buttons/Button'; -import Form from 'components/forms/Form'; -import formFieldInterface from 'interfaces/form_field'; -import InputFieldWithIcon from 'components/forms/fields/InputFieldWithIcon'; -import validate from 'components/forms/ResetPasswordForm/validate'; +import Button from "components/buttons/Button"; +import Form from "components/forms/Form"; +import formFieldInterface from "interfaces/form_field"; +import InputFieldWithIcon from "components/forms/fields/InputFieldWithIcon"; +import validate from "components/forms/ResetPasswordForm/validate"; -const baseClass = 'reset-password-form'; -const formFields = ['new_password', 'new_password_confirmation']; +const baseClass = "reset-password-form"; +const formFields = ["new_password", "new_password_confirmation"]; class ResetPasswordForm extends Component { static propTypes = { @@ -19,7 +19,7 @@ class ResetPasswordForm extends Component { }), }; - render () { + render() { const { fields, handleSubmit } = this.props; return ( diff --git a/frontend/components/forms/ResetPasswordForm/ResetPasswordForm.tests.jsx b/frontend/components/forms/ResetPasswordForm/ResetPasswordForm.tests.jsx index f4fe02a97..19795192f 100644 --- a/frontend/components/forms/ResetPasswordForm/ResetPasswordForm.tests.jsx +++ b/frontend/components/forms/ResetPasswordForm/ResetPasswordForm.tests.jsx @@ -1,78 +1,73 @@ -import React from 'react'; -import { mount } from 'enzyme'; -import { noop } from 'lodash'; +import React from "react"; +import { mount } from "enzyme"; +import { noop } from "lodash"; -import ResetPasswordForm from './ResetPasswordForm'; -import { fillInFormInput } from '../../../test/helpers'; +import ResetPasswordForm from "./ResetPasswordForm"; +import { fillInFormInput } from "../../../test/helpers"; -describe('ResetPasswordForm - component', () => { - const newPassword = 'p@ssw0rd'; +describe("ResetPasswordForm - component", () => { + const newPassword = "p@ssw0rd"; - it('updates component state when the new_password field is changed', () => { + it("updates component state when the new_password field is changed", () => { const form = mount(); - const newPasswordField = form.find({ name: 'new_password' }); + const newPasswordField = form.find({ name: "new_password" }); fillInFormInput(newPasswordField, newPassword); const { formData } = form.state(); expect(formData).toMatchObject({ new_password: newPassword }); }); - it( - 'updates component state when the new_password_confirmation field is changed', - () => { - const form = mount(); + it("updates component state when the new_password_confirmation field is changed", () => { + const form = mount(); - const newPasswordField = form.find({ name: 'new_password_confirmation' }); - fillInFormInput(newPasswordField, newPassword); + const newPasswordField = form.find({ name: "new_password_confirmation" }); + fillInFormInput(newPasswordField, newPassword); - const { formData } = form.state(); - expect(formData).toMatchObject({ new_password_confirmation: newPassword }); - }, - ); + const { formData } = form.state(); + expect(formData).toMatchObject({ new_password_confirmation: newPassword }); + }); - it( - 'it does not submit the form when the form fields have not been filled out', - () => { - const submitSpy = jest.fn(); - const form = mount(); - const submitBtn = form.find('button'); - - submitBtn.simulate('submit'); - - const { errors } = form.state(); - expect(errors.new_password).toEqual('New password field must be completed'); - expect(submitSpy).not.toHaveBeenCalled(); - }, - ); - - it( - 'it does not submit the form when only the new password field has been filled out', - () => { - const submitSpy = jest.fn(); - const form = mount(); - const newPasswordField = form.find({ name: 'new_password' }); - fillInFormInput(newPasswordField, newPassword); - const submitBtn = form.find('button'); - - submitBtn.simulate('submit'); - - const { errors } = form.state(); - expect(errors.new_password_confirmation).toEqual('New password confirmation field must be completed'); - expect(submitSpy).not.toHaveBeenCalled(); - }, - ); - - it('submits the form data when the form is submitted', () => { + it("it does not submit the form when the form fields have not been filled out", () => { const submitSpy = jest.fn(); const form = mount(); - const newPasswordField = form.find({ name: 'new_password' }); - const newPasswordConfirmationField = form.find({ name: 'new_password_confirmation' }); - const submitBtn = form.find('button'); + const submitBtn = form.find("button"); + + submitBtn.simulate("submit"); + + const { errors } = form.state(); + expect(errors.new_password).toEqual("New password field must be completed"); + expect(submitSpy).not.toHaveBeenCalled(); + }); + + it("it does not submit the form when only the new password field has been filled out", () => { + const submitSpy = jest.fn(); + const form = mount(); + const newPasswordField = form.find({ name: "new_password" }); + fillInFormInput(newPasswordField, newPassword); + const submitBtn = form.find("button"); + + submitBtn.simulate("submit"); + + const { errors } = form.state(); + expect(errors.new_password_confirmation).toEqual( + "New password confirmation field must be completed" + ); + expect(submitSpy).not.toHaveBeenCalled(); + }); + + it("submits the form data when the form is submitted", () => { + const submitSpy = jest.fn(); + const form = mount(); + const newPasswordField = form.find({ name: "new_password" }); + const newPasswordConfirmationField = form.find({ + name: "new_password_confirmation", + }); + const submitBtn = form.find("button"); fillInFormInput(newPasswordField, newPassword); fillInFormInput(newPasswordConfirmationField, newPassword); - submitBtn.simulate('submit'); + submitBtn.simulate("submit"); expect(submitSpy).toHaveBeenCalledWith({ new_password: newPassword, @@ -80,41 +75,43 @@ describe('ResetPasswordForm - component', () => { }); }); - it( - 'does not submit the form if the new password confirmation does not match', - () => { - const submitSpy = jest.fn(); - const form = mount(); - const newPasswordField = form.find({ name: 'new_password' }); - const newPasswordConfirmationField = form.find({ name: 'new_password_confirmation' }); - const submitBtn = form.find('button'); - - fillInFormInput(newPasswordField, newPassword); - fillInFormInput(newPasswordConfirmationField, 'not my new password'); - submitBtn.simulate('submit'); - - expect(submitSpy).not.toHaveBeenCalled(); - expect(form.state().errors).toMatchObject({ - new_password_confirmation: 'Passwords Do Not Match', - }); - }, - ); - - it('does not submit the form if the password is invalid', () => { + it("does not submit the form if the new password confirmation does not match", () => { const submitSpy = jest.fn(); const form = mount(); - const newPasswordField = form.find({ name: 'new_password' }); - const newPasswordConfirmationField = form.find({ name: 'new_password_confirmation' }); - const submitBtn = form.find('button'); - const invalidPassword = 'invalid'; + const newPasswordField = form.find({ name: "new_password" }); + const newPasswordConfirmationField = form.find({ + name: "new_password_confirmation", + }); + const submitBtn = form.find("button"); - fillInFormInput(newPasswordField, invalidPassword); - fillInFormInput(newPasswordConfirmationField, invalidPassword); - submitBtn.simulate('submit'); + fillInFormInput(newPasswordField, newPassword); + fillInFormInput(newPasswordConfirmationField, "not my new password"); + submitBtn.simulate("submit"); expect(submitSpy).not.toHaveBeenCalled(); expect(form.state().errors).toMatchObject({ - new_password: 'Password must be at least 7 characters and contain at least 1 letter, 1 number, and 1 symbol', + new_password_confirmation: "Passwords Do Not Match", + }); + }); + + it("does not submit the form if the password is invalid", () => { + const submitSpy = jest.fn(); + const form = mount(); + const newPasswordField = form.find({ name: "new_password" }); + const newPasswordConfirmationField = form.find({ + name: "new_password_confirmation", + }); + const submitBtn = form.find("button"); + const invalidPassword = "invalid"; + + fillInFormInput(newPasswordField, invalidPassword); + fillInFormInput(newPasswordConfirmationField, invalidPassword); + submitBtn.simulate("submit"); + + expect(submitSpy).not.toHaveBeenCalled(); + expect(form.state().errors).toMatchObject({ + new_password: + "Password must be at least 7 characters and contain at least 1 letter, 1 number, and 1 symbol", }); }); }); diff --git a/frontend/components/forms/ResetPasswordForm/index.js b/frontend/components/forms/ResetPasswordForm/index.js index 84ac128f0..21ff6c5a7 100644 --- a/frontend/components/forms/ResetPasswordForm/index.js +++ b/frontend/components/forms/ResetPasswordForm/index.js @@ -1 +1 @@ -export { default } from './ResetPasswordForm'; +export { default } from "./ResetPasswordForm"; diff --git a/frontend/components/forms/ResetPasswordForm/validate.js b/frontend/components/forms/ResetPasswordForm/validate.js index c79cd6ad8..775828f08 100644 --- a/frontend/components/forms/ResetPasswordForm/validate.js +++ b/frontend/components/forms/ResetPasswordForm/validate.js @@ -1,7 +1,7 @@ -import { size } from 'lodash'; -import validateEquality from 'components/forms/validators/validate_equality'; -import validatePresence from 'components/forms/validators/validate_presence'; -import validPassword from 'components/forms/validators/valid_password'; +import { size } from "lodash"; +import validateEquality from "components/forms/validators/validate_equality"; +import validatePresence from "components/forms/validators/validate_presence"; +import validPassword from "components/forms/validators/valid_password"; const validate = (formData) => { const errors = {}; @@ -10,24 +10,27 @@ const validate = (formData) => { new_password_confirmation: newPasswordConfirmation, } = formData; - const noMatch = newPassword && + const noMatch = + newPassword && newPasswordConfirmation && !validateEquality(newPassword, newPasswordConfirmation); if (!validPassword(newPassword)) { - errors.new_password = 'Password must be at least 7 characters and contain at least 1 letter, 1 number, and 1 symbol'; + errors.new_password = + "Password must be at least 7 characters and contain at least 1 letter, 1 number, and 1 symbol"; } if (!validatePresence(newPasswordConfirmation)) { - errors.new_password_confirmation = 'New password confirmation field must be completed'; + errors.new_password_confirmation = + "New password confirmation field must be completed"; } if (!validatePresence(newPassword)) { - errors.new_password = 'New password field must be completed'; + errors.new_password = "New password field must be completed"; } if (noMatch) { - errors.new_password_confirmation = 'Passwords Do Not Match'; + errors.new_password_confirmation = "Passwords Do Not Match"; } const valid = !size(errors); diff --git a/frontend/components/forms/UserSettingsForm/UserSettingsForm.jsx b/frontend/components/forms/UserSettingsForm/UserSettingsForm.jsx index 06209e4a9..204ec5a81 100644 --- a/frontend/components/forms/UserSettingsForm/UserSettingsForm.jsx +++ b/frontend/components/forms/UserSettingsForm/UserSettingsForm.jsx @@ -1,14 +1,14 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; +import React, { Component } from "react"; +import PropTypes from "prop-types"; -import Button from 'components/buttons/Button'; -import Form from 'components/forms/Form'; -import formFieldInterface from 'interfaces/form_field'; -import InputField from 'components/forms/fields/InputField'; +import Button from "components/buttons/Button"; +import Form from "components/forms/Form"; +import formFieldInterface from "interfaces/form_field"; +import InputField from "components/forms/fields/InputField"; -const formFields = ['email', 'name', 'position', 'username']; +const formFields = ["email", "name", "position", "username"]; -const baseClass = 'manage-user'; +const baseClass = "manage-user"; class UserSettingsForm extends Component { static propTypes = { @@ -35,9 +35,9 @@ class UserSettingsForm extends Component { Pending change to {pendingEmail} ); - } + }; - render () { + render() { const { fields, handleSubmit, onCancel } = this.props; const { renderEmailHint } = this; @@ -53,17 +53,15 @@ class UserSettingsForm extends Component { label="Email (required)" hint={renderEmailHint()} /> - - + +
    - - + +
    ); diff --git a/frontend/components/forms/UserSettingsForm/UserSettingsForm.tests.jsx b/frontend/components/forms/UserSettingsForm/UserSettingsForm.tests.jsx index 39562aa5b..1ab6e7bf1 100644 --- a/frontend/components/forms/UserSettingsForm/UserSettingsForm.tests.jsx +++ b/frontend/components/forms/UserSettingsForm/UserSettingsForm.tests.jsx @@ -1,46 +1,54 @@ -import React from 'react'; -import { mount } from 'enzyme'; -import { noop } from 'lodash'; +import React from "react"; +import { mount } from "enzyme"; +import { noop } from "lodash"; -import UserSettingsForm from 'components/forms/UserSettingsForm'; -import helpers from 'test/helpers'; +import UserSettingsForm from "components/forms/UserSettingsForm"; +import helpers from "test/helpers"; const { fillInFormInput, itBehavesLikeAFormInputElement } = helpers; -describe('UserSettingsForm - component', () => { +describe("UserSettingsForm - component", () => { const defaultProps = { handleSubmit: noop, onCancel: noop, }; - it('has the correct fields', () => { + it("has the correct fields", () => { const form = mount(); - itBehavesLikeAFormInputElement(form, 'email'); - itBehavesLikeAFormInputElement(form, 'name'); - itBehavesLikeAFormInputElement(form, 'username'); + itBehavesLikeAFormInputElement(form, "email"); + itBehavesLikeAFormInputElement(form, "name"); + itBehavesLikeAFormInputElement(form, "username"); }); - it('calls the handleSubmit props with form data', () => { + it("calls the handleSubmit props with form data", () => { const handleSubmitSpy = jest.fn(); const props = { ...defaultProps, handleSubmit: handleSubmitSpy }; const form = mount(); - const expectedFormData = { email: 'email@example.com', name: 'Jim Example', username: 'jimmyexamples' }; - const emailInput = form.find({ name: 'email' }).find('input'); - const nameInput = form.find({ name: 'name' }).find('input'); - const usernameInput = form.find({ name: 'username' }).find('input'); + const expectedFormData = { + email: "email@example.com", + name: "Jim Example", + username: "jimmyexamples", + }; + const emailInput = form.find({ name: "email" }).find("input"); + const nameInput = form.find({ name: "name" }).find("input"); + const usernameInput = form.find({ name: "username" }).find("input"); fillInFormInput(emailInput, expectedFormData.email); fillInFormInput(nameInput, expectedFormData.name); fillInFormInput(usernameInput, expectedFormData.username); - form.find('form').simulate('submit'); + form.find("form").simulate("submit"); expect(handleSubmitSpy).toHaveBeenCalledWith(expectedFormData); }); - it('initializes the form with the users data', () => { - const user = { email: 'email@example.com', name: 'Jim Example', username: 'jimmyexamples' }; + it("initializes the form with the users data", () => { + const user = { + email: "email@example.com", + name: "Jim Example", + username: "jimmyexamples", + }; const props = { ...defaultProps, formData: user }; const form = mount(); diff --git a/frontend/components/forms/UserSettingsForm/_styles.scss b/frontend/components/forms/UserSettingsForm/_styles.scss index c946c22a6..a8182ac7f 100644 --- a/frontend/components/forms/UserSettingsForm/_styles.scss +++ b/frontend/components/forms/UserSettingsForm/_styles.scss @@ -21,7 +21,6 @@ justify-content: flex-end; .button { - &:last-child { margin-left: 16px; } diff --git a/frontend/components/forms/UserSettingsForm/index.js b/frontend/components/forms/UserSettingsForm/index.js index e4a795658..9e00c9d6d 100644 --- a/frontend/components/forms/UserSettingsForm/index.js +++ b/frontend/components/forms/UserSettingsForm/index.js @@ -1 +1 @@ -export { default } from './UserSettingsForm'; +export { default } from "./UserSettingsForm"; diff --git a/frontend/components/forms/admin/AppConfigForm/AppConfigForm.jsx b/frontend/components/forms/admin/AppConfigForm/AppConfigForm.jsx index 583c21343..62f1b3565 100644 --- a/frontend/components/forms/admin/AppConfigForm/AppConfigForm.jsx +++ b/frontend/components/forms/admin/AppConfigForm/AppConfigForm.jsx @@ -1,41 +1,70 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; +import React, { Component } from "react"; +import PropTypes from "prop-types"; -import Button from 'components/buttons/Button'; -import Checkbox from 'components/forms/fields/Checkbox'; -import Dropdown from 'components/forms/fields/Dropdown'; -import Form from 'components/forms/Form'; -import formFieldInterface from 'interfaces/form_field'; -import enrollSecretInterface from 'interfaces/enroll_secret'; -import EnrollSecretTable from 'components/config/EnrollSecretTable'; -import KolideIcon from 'components/icons/KolideIcon'; -import InputField from 'components/forms/fields/InputField'; -import OrgLogoIcon from 'components/icons/OrgLogoIcon'; -import Slider from 'components/forms/fields/Slider'; -import validate from 'components/forms/admin/AppConfigForm/validate'; -import IconToolTip from 'components/IconToolTip'; +import Button from "components/buttons/Button"; +import Checkbox from "components/forms/fields/Checkbox"; +import Dropdown from "components/forms/fields/Dropdown"; +import Form from "components/forms/Form"; +import formFieldInterface from "interfaces/form_field"; +import enrollSecretInterface from "interfaces/enroll_secret"; +import EnrollSecretTable from "components/config/EnrollSecretTable"; +import KolideIcon from "components/icons/KolideIcon"; +import InputField from "components/forms/fields/InputField"; +import OrgLogoIcon from "components/icons/OrgLogoIcon"; +import Slider from "components/forms/fields/Slider"; +import validate from "components/forms/admin/AppConfigForm/validate"; +import IconToolTip from "components/IconToolTip"; const authMethodOptions = [ - { label: 'Plain', value: 'authmethod_plain' }, - { label: 'Cram MD5', value: 'authmethod_cram_md5' }, - { label: 'Login', value: 'authmethod_login' }, + { label: "Plain", value: "authmethod_plain" }, + { label: "Cram MD5", value: "authmethod_cram_md5" }, + { label: "Login", value: "authmethod_login" }, ]; const authTypeOptions = [ - { label: 'Username and Password', value: 'authtype_username_password' }, - { label: 'None', value: 'authtype_none' }, + { label: "Username and Password", value: "authtype_username_password" }, + { label: "None", value: "authtype_none" }, ]; -const baseClass = 'app-config-form'; +const baseClass = "app-config-form"; const formFields = [ - 'authentication_method', 'authentication_type', 'domain', 'enable_ssl_tls', 'enable_start_tls', 'kolide_server_url', - 'org_logo_url', 'org_name', 'osquery_enroll_secret', 'password', 'port', 'sender_address', - 'server', 'user_name', 'verify_ssl_certs', 'idp_name', 'entity_id', 'issuer_uri', 'idp_image_url', - 'metadata', 'metadata_url', 'enable_sso', 'enable_sso_idp_login', 'enable_smtp', 'host_expiry_enabled', - 'host_expiry_window', 'live_query_disabled', + "authentication_method", + "authentication_type", + "domain", + "enable_ssl_tls", + "enable_start_tls", + "kolide_server_url", + "org_logo_url", + "org_name", + "osquery_enroll_secret", + "password", + "port", + "sender_address", + "server", + "user_name", + "verify_ssl_certs", + "idp_name", + "entity_id", + "issuer_uri", + "idp_image_url", + "metadata", + "metadata_url", + "enable_sso", + "enable_sso_idp_login", + "enable_smtp", + "host_expiry_enabled", + "host_expiry_window", + "live_query_disabled", ]; const Header = ({ showAdvancedOptions }) => { - const CaratIcon = ; + const CaratIcon = ( + + ); - return Advanced Options {CaratIcon} Most users do not need to modify these options.; + return ( + + Advanced Options {CaratIcon}{" "} + Most users do not need to modify these options. + + ); }; Header.propTypes = { showAdvancedOptions: PropTypes.bool.isRequired }; @@ -75,7 +104,7 @@ class AppConfigForm extends Component { smtpConfigured: PropTypes.bool.isRequired, }; - constructor (props) { + constructor(props) { super(props); this.state = { showAdvancedOptions: false }; @@ -89,7 +118,7 @@ class AppConfigForm extends Component { this.setState({ showAdvancedOptions: !showAdvancedOptions }); return false; - } + }; renderAdvancedOptions = () => { const { fields } = this.props; @@ -107,41 +136,47 @@ class AppConfigForm extends Component { - - + +
    Domain - If you need to specify a HELO domain, you can do it here (Default: Blank)

    \

    Verify SSL Certs - Turn this off (not recommended) if you use a self-signed certificate (Default: On)

    \

    Enable STARTTLS - Detects if STARTTLS is enabled in your SMTP server and starts to use it. (Default: On)

    \

    Host Expiry - When enabled, allows automatic cleanup of hosts that have not communicated with Fleet in some number of days. (Default: Off)

    \

    Host Expiry Window - If a host has not communicated with Fleet in the specified number of days, it will be removed.

    \

    Disable Live Queries - When enabled, disables the ability to run live queries (ad hoc queries executed via the UI or fleetctl). (Default: Off)

    \ - '} + ' + } />
    ); - } + }; renderSmtpSection = () => { const { fields } = this.props; - if (fields.authentication_type.value === 'authtype_none') { + if (fields.authentication_type.value === "authtype_none") { return false; } return (
    - +
    ); - } + }; - render () { + render() { const { fields, handleSubmit, smtpConfigured, enrollSecret } = this.props; - const { onToggleAdvancedOptions, renderAdvancedOptions, renderSmtpSection } = this; + const { + onToggleAdvancedOptions, + renderAdvancedOptions, + renderSmtpSection, + } = this; const { showAdvancedOptions } = this.state; return (
    -

    Organization Info

    +

    + Organization Info +

    - +
    -

    Fleet web address

    +

    + Fleet web address +

    Include base path only (eg. no /v1)} + hint={ + + Include base path only (eg. no /v1) + + } />
    - +
    -

    SAML Single Sign On Options

    +

    + SAML Single Sign On Options +

    - - Enable Single Sign On - + Enable Single Sign On
    - +
    - +
    The URI you provide here must exactly match the Entity ID field used in identity provider configuration.} + hint={ + + The URI you provide here must exactly match the Entity ID + field used in identity provider configuration. + + } />
    - +
    - - +
    - +
    - +
    - +
    - +
    - +
    If available from the identity provider, this is the preferred means of providing metadata.} + hint={ + + If available from the identity provider, this is the preferred + means of providing metadata. + + } />
    - +
    - + Allow SSO login initiated by Identity Provider
    @@ -280,44 +338,42 @@ class AppConfigForm extends Component {

    - SMTP Options STATUS: {smtpConfigured ? 'CONFIGURED' : 'NOT CONFIGURED'} + + SMTP Options{" "} + + STATUS:{" "} + {smtpConfigured ? "CONFIGURED" : "NOT CONFIGURED"} + +

    - - Enable SMTP - + Enable SMTP
    - +
    - +
    - - - + + + Use SSL/TLS to connect (recommended)
    - +
    @@ -331,16 +387,20 @@ class AppConfigForm extends Component {
    If your mail server requires authentication, you need to specify the authentication type here.

    \

    No Authentication - Select this if your SMTP is open.

    \

    Username & Password - Select this if your SMTP server requires authentication with a username and password.

    \ - '} + " + } />
    -

    Osquery Enrollment Secrets

    +

    + Osquery Enrollment Secrets +

    Manage secrets with fleetctl. Active secrets: @@ -349,13 +409,18 @@ class AppConfigForm extends Component {

    -

    +

    + +
    + +

    {renderAdvancedOptions()}
    - diff --git a/frontend/components/forms/admin/AppConfigForm/AppConfigForm.tests.jsx b/frontend/components/forms/admin/AppConfigForm/AppConfigForm.tests.jsx index 1c370b8d7..e6e0c03ce 100644 --- a/frontend/components/forms/admin/AppConfigForm/AppConfigForm.tests.jsx +++ b/frontend/components/forms/admin/AppConfigForm/AppConfigForm.tests.jsx @@ -1,121 +1,123 @@ -import React from 'react'; -import { mount } from 'enzyme'; -import { noop } from 'lodash'; +import React from "react"; +import { mount } from "enzyme"; +import { noop } from "lodash"; -import AppConfigForm from 'components/forms/admin/AppConfigForm'; -import { itBehavesLikeAFormInputElement } from 'test/helpers'; +import AppConfigForm from "components/forms/admin/AppConfigForm"; +import { itBehavesLikeAFormInputElement } from "test/helpers"; -describe('AppConfigForm - form', () => { +describe("AppConfigForm - form", () => { const defaultProps = { - formData: { org_name: 'Kolide' }, + formData: { org_name: "Kolide" }, handleSubmit: noop, smtpConfigured: false, enrollSecret: [ - { name: 'foo', secret: 'foo_secret', active: true }, - { name: 'bar', secret: 'bar_secret', active: true }, - { name: 'inactive', secret: 'inactive', active: false }, + { name: "foo", secret: "foo_secret", active: true }, + { name: "bar", secret: "bar_secret", active: true }, + { name: "inactive", secret: "inactive", active: false }, ], }; const form = mount(); - describe('Organization Name input', () => { - it('renders an input field', () => { - itBehavesLikeAFormInputElement(form, 'org_name'); + describe("Organization Name input", () => { + it("renders an input field", () => { + itBehavesLikeAFormInputElement(form, "org_name"); }); }); - describe('Organization Avatar input', () => { - it('renders an input field', () => { - itBehavesLikeAFormInputElement(form, 'org_logo_url'); + describe("Organization Avatar input", () => { + it("renders an input field", () => { + itBehavesLikeAFormInputElement(form, "org_logo_url"); }); }); - describe('Fleet App URL input', () => { - it('renders an input field', () => { - itBehavesLikeAFormInputElement(form, 'kolide_server_url'); + describe("Fleet App URL input", () => { + it("renders an input field", () => { + itBehavesLikeAFormInputElement(form, "kolide_server_url"); }); }); - describe('Sender Address input', () => { - it('renders an input field', () => { - itBehavesLikeAFormInputElement(form, 'sender_address'); + describe("Sender Address input", () => { + it("renders an input field", () => { + itBehavesLikeAFormInputElement(form, "sender_address"); }); }); - describe('SMTP Server input', () => { - it('renders an input field', () => { - itBehavesLikeAFormInputElement(form, 'server'); + describe("SMTP Server input", () => { + it("renders an input field", () => { + itBehavesLikeAFormInputElement(form, "server"); }); }); - describe('Port input', () => { - it('renders an input field', () => { - itBehavesLikeAFormInputElement(form, 'port'); + describe("Port input", () => { + it("renders an input field", () => { + itBehavesLikeAFormInputElement(form, "port"); }); }); - describe('Enable SSL/TLS input', () => { - it('renders an input field', () => { - itBehavesLikeAFormInputElement(form, 'enable_ssl_tls', 'Checkbox'); + describe("Enable SSL/TLS input", () => { + it("renders an input field", () => { + itBehavesLikeAFormInputElement(form, "enable_ssl_tls", "Checkbox"); }); }); - describe('SMTP user name input', () => { - it('renders an input field', () => { - itBehavesLikeAFormInputElement(form, 'user_name'); + describe("SMTP user name input", () => { + it("renders an input field", () => { + itBehavesLikeAFormInputElement(form, "user_name"); }); }); - describe('SMTP user password input', () => { - it('renders an HTML password input', () => { + describe("SMTP user password input", () => { + it("renders an HTML password input", () => { const passwordField = form.find('input[name="password"]'); - expect(passwordField.prop('type')).toEqual('password'); + expect(passwordField.prop("type")).toEqual("password"); }); - it('renders an input field', () => { - itBehavesLikeAFormInputElement(form, 'password'); + it("renders an input field", () => { + itBehavesLikeAFormInputElement(form, "password"); }); }); - describe('Enroll Secret', () => { - it('renders enroll secrets table', () => { - expect(form.find('EnrollSecretTable').length).toEqual(1); + describe("Enroll Secret", () => { + it("renders enroll secrets table", () => { + expect(form.find("EnrollSecretTable").length).toEqual(1); }); }); - - it('does not render advanced options by default', () => { - expect(form.find({ name: 'domain' }).length).toEqual(0); - expect(form.find('Slider').length).toEqual(0); + it("does not render advanced options by default", () => { + expect(form.find({ name: "domain" }).length).toEqual(0); + expect(form.find("Slider").length).toEqual(0); }); - describe('Advanced options', () => { + describe("Advanced options", () => { beforeAll(() => { - form.find('.app-config-form__show-options').simulate('click'); + form.find(".app-config-form__show-options").simulate("click"); }); it('renders advanced options when "Advanced Options" is clicked', () => { - expect(form.find({ name: 'domain' }).hostNodes().length).toEqual(1); - expect(form.find('Slider').length).toEqual(4); + expect(form.find({ name: "domain" }).hostNodes().length).toEqual(1); + expect(form.find("Slider").length).toEqual(4); }); - it('disables host expiry window by default', () => { - const InputField = form.find({ name: 'host_expiry_window' }); - const inputElement = InputField.find('input'); + it("disables host expiry window by default", () => { + const InputField = form.find({ name: "host_expiry_window" }); + const inputElement = InputField.find("input"); expect(inputElement.length).toEqual(1); - expect(inputElement.hasClass('input-field--disabled')).toBe(true); + expect(inputElement.hasClass("input-field--disabled")).toBe(true); }); - it('enables host expiry window', () => { - form.find({ name: 'host_expiry_enabled' }).find('button').simulate('click'); - const InputField = form.find({ name: 'host_expiry_window' }); - const inputElement = InputField.find('input'); - expect(inputElement.hasClass('input-field--disabled')).toBe(false); + it("enables host expiry window", () => { + form + .find({ name: "host_expiry_enabled" }) + .find("button") + .simulate("click"); + const InputField = form.find({ name: "host_expiry_window" }); + const inputElement = InputField.find("input"); + expect(inputElement.hasClass("input-field--disabled")).toBe(false); }); - it('renders live query disabled input', () => { - form.find({ name: 'live_query_disabled' }); + it("renders live query disabled input", () => { + form.find({ name: "live_query_disabled" }); expect(form.length).toEqual(1); }); }); diff --git a/frontend/components/forms/admin/AppConfigForm/_styles.scss b/frontend/components/forms/admin/AppConfigForm/_styles.scss index 8394b242f..03aa6b766 100644 --- a/frontend/components/forms/admin/AppConfigForm/_styles.scss +++ b/frontend/components/forms/admin/AppConfigForm/_styles.scss @@ -1,5 +1,4 @@ .app-config-form { - &__enroll-secret-label { font-size: 15px; diff --git a/frontend/components/forms/admin/AppConfigForm/index.js b/frontend/components/forms/admin/AppConfigForm/index.js index 971895f30..4aa876acb 100644 --- a/frontend/components/forms/admin/AppConfigForm/index.js +++ b/frontend/components/forms/admin/AppConfigForm/index.js @@ -1 +1 @@ -export { default } from './AppConfigForm'; +export { default } from "./AppConfigForm"; diff --git a/frontend/components/forms/admin/AppConfigForm/validate.js b/frontend/components/forms/admin/AppConfigForm/validate.js index 0e57908a8..32b019827 100644 --- a/frontend/components/forms/admin/AppConfigForm/validate.js +++ b/frontend/components/forms/admin/AppConfigForm/validate.js @@ -1,4 +1,4 @@ -import { size } from 'lodash'; +import { size } from "lodash"; export default (formData) => { const errors = {}; @@ -23,51 +23,52 @@ export default (formData) => { if (enableSSO) { if (!metadata && !metadataURL) { - errors.metadata_url = 'Metadata URL must be present'; + errors.metadata_url = "Metadata URL must be present"; } if (!entityID) { - errors.entity_id = 'Entity ID must be present'; + errors.entity_id = "Entity ID must be present"; } if (!idpName) { - errors.idp_name = 'Identity Provider Name must be present'; + errors.idp_name = "Identity Provider Name must be present"; } } if (!kolideServerUrl) { - errors.kolide_server_url = 'Fleet Server URL must be present'; + errors.kolide_server_url = "Fleet Server URL must be present"; } if (!orgName) { - errors.org_name = 'Organization Name must be present'; + errors.org_name = "Organization Name must be present"; } if (enableSMTP) { if (!smtpSenderAddress) { - errors.sender_address = 'SMTP Sender Address must be present'; + errors.sender_address = "SMTP Sender Address must be present"; } if (!smtpServer) { - errors.server = 'SMTP Server must be present'; + errors.server = "SMTP Server must be present"; } if (!smtpServerPort) { - errors.server = 'SMTP Server Port must be present'; + errors.server = "SMTP Server Port must be present"; } - if (authType !== 'authtype_none') { + if (authType !== "authtype_none") { if (!smtpUserName) { - errors.user_name = 'SMTP Username must be present'; + errors.user_name = "SMTP Username must be present"; } if (!smtpPassword) { - errors.password = 'SMTP Password must be present'; + errors.password = "SMTP Password must be present"; } } } if (hostExpiryEnabled) { if (isNaN(hostExpiryWindow) || Number(hostExpiryWindow) <= 0) { - errors.host_expiry_window = 'Host Expiry Window must be a positive number'; + errors.host_expiry_window = + "Host Expiry Window must be a positive number"; } } diff --git a/frontend/components/forms/admin/AppConfigForm/validate.tests.js b/frontend/components/forms/admin/AppConfigForm/validate.tests.js index 0d5293755..74efd970e 100644 --- a/frontend/components/forms/admin/AppConfigForm/validate.tests.js +++ b/frontend/components/forms/admin/AppConfigForm/validate.tests.js @@ -1,91 +1,91 @@ -import validate from 'components/forms/admin/AppConfigForm/validate'; +import validate from "components/forms/admin/AppConfigForm/validate"; -describe('AppConfigForm - validations', () => { +describe("AppConfigForm - validations", () => { const validFormData = { - org_name: 'The Gnar Co.', - authentication_type: 'username_password', - kolide_server_url: 'https://gnar.dog', - sender_address: 'hi@gnar.dog', + org_name: "The Gnar Co.", + authentication_type: "username_password", + kolide_server_url: "https://gnar.dog", + sender_address: "hi@gnar.dog", enable_smtp: true, - server: '192.168.99.100', - port: '1025', - user_name: 'gnardog', - password: 'p@ssw0rd', + server: "192.168.99.100", + port: "1025", + user_name: "gnardog", + password: "p@ssw0rd", host_expiry_enabled: true, - host_expiry_window: '42', + host_expiry_window: "42", }; - it('returns a valid object when the form data is valid', () => { + it("returns a valid object when the form data is valid", () => { expect(validate(validFormData)).toEqual({ valid: true, errors: {} }); }); - it('validates presence of the org_name field', () => { + it("validates presence of the org_name field", () => { const invalidFormData = { ...validFormData, - org_name: '', + org_name: "", }; expect(validate(invalidFormData)).toEqual({ valid: false, errors: { - org_name: 'Organization Name must be present', + org_name: "Organization Name must be present", }, }); }); - it('validates presence of the kolide_server_url field', () => { + it("validates presence of the kolide_server_url field", () => { const invalidFormData = { ...validFormData, - kolide_server_url: '', + kolide_server_url: "", }; expect(validate(invalidFormData)).toEqual({ valid: false, errors: { - kolide_server_url: 'Fleet Server URL must be present', + kolide_server_url: "Fleet Server URL must be present", }, }); }); - describe('smtp configurations', () => { - it('validates the sender address', () => { + describe("smtp configurations", () => { + it("validates the sender address", () => { const invalidFormData = { ...validFormData, - sender_address: '', + sender_address: "", }; expect(validate(invalidFormData)).toEqual({ valid: false, errors: { - sender_address: 'SMTP Sender Address must be present', + sender_address: "SMTP Sender Address must be present", }, }); }); - it('validates the smtp server', () => { + it("validates the smtp server", () => { const invalidFormData = { ...validFormData, - server: '', + server: "", }; expect(validate(invalidFormData)).toEqual({ valid: false, errors: { - server: 'SMTP Server must be present', + server: "SMTP Server must be present", }, }); }); - it('validates the smtp port', () => { + it("validates the smtp port", () => { const invalidFormData = { ...validFormData, - port: '', + port: "", }; expect(validate(invalidFormData)).toEqual({ valid: false, errors: { - server: 'SMTP Server Port must be present', + server: "SMTP Server Port must be present", }, }); }); @@ -93,13 +93,13 @@ describe('AppConfigForm - validations', () => { it('validates the password if auth type is not "none"', () => { const invalidFormData = { ...validFormData, - password: '', + password: "", }; expect(validate(invalidFormData)).toEqual({ valid: false, errors: { - password: 'SMTP Password must be present', + password: "SMTP Password must be present", }, }); }); @@ -107,38 +107,38 @@ describe('AppConfigForm - validations', () => { it('validates the user_name if auth type is not "none"', () => { const invalidFormData = { ...validFormData, - user_name: '', + user_name: "", }; expect(validate(invalidFormData)).toEqual({ valid: false, errors: { - user_name: 'SMTP Username must be present', + user_name: "SMTP Username must be present", }, }); }); - it('does not validate smtp config if smtp not enabled', () => { + it("does not validate smtp config if smtp not enabled", () => { const formData = { ...validFormData, enable_smtp: false, - user_name: '', - server: '', - sender_address: '', - password: '********', - port: '587', + user_name: "", + server: "", + sender_address: "", + password: "********", + port: "587", }; const invalidFormData = { ...validFormData, - user_name: '', - server: '', - sender_address: '', - password: 'newPassword', - port: '587', + user_name: "", + server: "", + sender_address: "", + password: "newPassword", + port: "587", }; const missingPortFormData = { ...validFormData, - port: '', + port: "", }; expect(validate(formData)).toEqual({ @@ -149,58 +149,70 @@ describe('AppConfigForm - validations', () => { expect(validate(invalidFormData)).toEqual({ valid: false, errors: { - sender_address: 'SMTP Sender Address must be present', - server: 'SMTP Server must be present', - user_name: 'SMTP Username must be present', + sender_address: "SMTP Sender Address must be present", + server: "SMTP Server must be present", + user_name: "SMTP Username must be present", }, }); expect(validate(missingPortFormData)).toEqual({ valid: false, errors: { - server: 'SMTP Server Port must be present', + server: "SMTP Server Port must be present", }, }); }); - it( - 'does not validate the user_name and password if the auth type is "none"', - () => { - const formData = { - ...validFormData, - authentication_type: 'authtype_none', - password: '', - user_name: '', - }; + it('does not validate the user_name and password if the auth type is "none"', () => { + const formData = { + ...validFormData, + authentication_type: "authtype_none", + password: "", + user_name: "", + }; - expect(validate(formData)).toEqual({ valid: true, errors: {} }); - }, - ); + expect(validate(formData)).toEqual({ valid: true, errors: {} }); + }); }); - describe('host expiry settings', () => { - it('does not validate missing expiry window', () => { + describe("host expiry settings", () => { + it("does not validate missing expiry window", () => { const formData = { ...validFormData, }; delete formData.host_expiry_window; - expect(validate(formData)).toEqual({ valid: false, errors: { host_expiry_window: 'Host Expiry Window must be a positive number' } }); + expect(validate(formData)).toEqual({ + valid: false, + errors: { + host_expiry_window: "Host Expiry Window must be a positive number", + }, + }); }); - it('does not validate NaN expiry window', () => { + it("does not validate NaN expiry window", () => { const formData = { ...validFormData, - host_expiry_window: 'abcd', + host_expiry_window: "abcd", }; - expect(validate(formData)).toEqual({ valid: false, errors: { host_expiry_window: 'Host Expiry Window must be a positive number' } }); + expect(validate(formData)).toEqual({ + valid: false, + errors: { + host_expiry_window: "Host Expiry Window must be a positive number", + }, + }); }); - it('does not validate negative expiry window', () => { + it("does not validate negative expiry window", () => { const formData = { ...validFormData, - host_expiry_window: '-21', + host_expiry_window: "-21", }; - expect(validate(formData)).toEqual({ valid: false, errors: { host_expiry_window: 'Host Expiry Window must be a positive number' } }); + expect(validate(formData)).toEqual({ + valid: false, + errors: { + host_expiry_window: "Host Expiry Window must be a positive number", + }, + }); }); }); }); diff --git a/frontend/components/forms/admin/EditUserForm/EditUserForm.jsx b/frontend/components/forms/admin/EditUserForm/EditUserForm.jsx index 7b80ec3df..5ac27ecd9 100644 --- a/frontend/components/forms/admin/EditUserForm/EditUserForm.jsx +++ b/frontend/components/forms/admin/EditUserForm/EditUserForm.jsx @@ -1,13 +1,13 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import Checkbox from 'components/forms/fields/Checkbox'; -import Button from 'components/buttons/Button'; -import Form from 'components/forms/Form'; -import formFieldInterface from 'interfaces/form_field'; -import InputField from 'components/forms/fields/InputField'; +import React, { Component } from "react"; +import PropTypes from "prop-types"; +import Checkbox from "components/forms/fields/Checkbox"; +import Button from "components/buttons/Button"; +import Form from "components/forms/Form"; +import formFieldInterface from "interfaces/form_field"; +import InputField from "components/forms/fields/InputField"; -const baseClass = 'edit-user-form'; -const fieldNames = ['email', 'name', 'position', 'username', 'sso_enabled']; +const baseClass = "edit-user-form"; +const fieldNames = ["email", "name", "position", "username", "sso_enabled"]; class EditUserForm extends Component { static propTypes = { @@ -23,7 +23,7 @@ class EditUserForm extends Component { }).isRequired, }; - render () { + render() { const { fields, handleSubmit, onCancel, isCurrentUser } = this.props; return ( @@ -57,11 +57,7 @@ class EditUserForm extends Component { labelClassName={`${baseClass}__label`} inputClassName={`${baseClass}__input ${baseClass}__input--email`} /> - - Enable Single Sign On - + Enable Single Sign On
    @@ -60,6 +56,6 @@ class OsqueryOptionsForm extends Component { } export default Form(OsqueryOptionsForm, { - fields: ['osquery_options'], + fields: ["osquery_options"], validate, }); diff --git a/frontend/components/forms/admin/OsqueryOptionsForm/index.js b/frontend/components/forms/admin/OsqueryOptionsForm/index.js index 689d7e2b4..fe74d50f1 100644 --- a/frontend/components/forms/admin/OsqueryOptionsForm/index.js +++ b/frontend/components/forms/admin/OsqueryOptionsForm/index.js @@ -1 +1 @@ -export { default } from './OsqueryOptionsForm'; +export { default } from "./OsqueryOptionsForm"; diff --git a/frontend/components/forms/fields/Checkbox/Checkbox.jsx b/frontend/components/forms/fields/Checkbox/Checkbox.jsx index 8f68a4700..6729e668b 100644 --- a/frontend/components/forms/fields/Checkbox/Checkbox.jsx +++ b/frontend/components/forms/fields/Checkbox/Checkbox.jsx @@ -1,11 +1,11 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import classnames from 'classnames'; -import { noop, pick } from 'lodash'; +import React, { Component } from "react"; +import PropTypes from "prop-types"; +import classnames from "classnames"; +import { noop, pick } from "lodash"; -import FormField from 'components/forms/FormField'; +import FormField from "components/forms/FormField"; -const baseClass = 'kolide-checkbox'; +const baseClass = "kolide-checkbox"; class Checkbox extends Component { static propTypes = { @@ -29,19 +29,30 @@ class Checkbox extends Component { return onChange(!value); }; - render () { + render() { const { handleChange } = this; - const { children, className, disabled, name, value, wrapperClassName } = this.props; + const { + children, + className, + disabled, + name, + value, + wrapperClassName, + } = this.props; const checkBoxClass = classnames(baseClass, className); - const formFieldProps = pick(this.props, ['hint', 'label', 'error', 'name']); + const formFieldProps = pick(this.props, ["hint", "label", "error", "name"]); const checkBoxTickClass = classnames(`${checkBoxClass}__tick`, { [`${checkBoxClass}__tick--disabled`]: disabled, }); return ( - +