diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 8346ae6c8..2fd7571f3 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -19,13 +19,14 @@ jobs: run: sudo apt-get update && sudo apt-get install -y python3-setuptools python3-pip chromium-browser libgbm1 && make install-deps - name: DB setup run: | + cp cres/db.sqlite standards_cache.sqlite make migrate-upgrade - - name: Run App in background + - name: Run app and e2e tests run: | yarn build [ -d "./venv" ] && . ./venv/bin/activate export FLASK_APP=./cre.py export FLASK_CONFIG=development - FLASK_CONFIG=development flask run& - - name: Test-e2e - run: yarn test:e2e \ No newline at end of file + FLASK_CONFIG=development flask run & + sleep 20s + yarn test:e2e \ No newline at end of file diff --git a/application/database/db.py b/application/database/db.py index ae007d224..23c5db66a 100644 --- a/application/database/db.py +++ b/application/database/db.py @@ -13,6 +13,7 @@ StructuredRel, db, ) +import neo4j from sqlalchemy.orm import aliased import os import logging @@ -547,7 +548,11 @@ def standards(self) -> List[str]: @classmethod def everything(self) -> List[str]: - return [NEO_DB.parse_node(rec) for rec in NeoDocument.nodes.all()] + try: + return [NEO_DB.parse_node(rec) for rec in NeoDocument.nodes.all()] + except neo4j.exceptions.ServiceUnavailable: + logger.error("Neo4j DB offline") + return None @staticmethod def parse_node(node: NeoDocument) -> cre_defs.Document: diff --git a/application/frontend/src/pages/Search/components/SearchBar.tsx b/application/frontend/src/pages/Search/components/SearchBar.tsx index 2989ed3be..3182eb7a8 100644 --- a/application/frontend/src/pages/Search/components/SearchBar.tsx +++ b/application/frontend/src/pages/Search/components/SearchBar.tsx @@ -44,13 +44,11 @@ export const SearchBar = () => { term: e.target.value, }); }} - label={ - - } - labelPosition="right" + action={{ + icon: 'search', + content: 'Search', + color: 'primary', + }} placeholder="Search..." /> diff --git a/application/frontend/src/test/basic-e2etest.ts b/application/frontend/src/test/basic-e2e.test.ts similarity index 83% rename from application/frontend/src/test/basic-e2etest.ts rename to application/frontend/src/test/basic-e2e.test.ts index e6dbe1caa..2a982bffa 100644 --- a/application/frontend/src/test/basic-e2etest.ts +++ b/application/frontend/src/test/basic-e2e.test.ts @@ -14,6 +14,7 @@ describe('App.js', () => { jest.setTimeout(1000000); browser = await puppeteer.launch(debug); page = await browser.newPage(); + page.setDefaultTimeout(15000) }); it('contains the welcome text', async () => { @@ -25,26 +26,24 @@ describe('App.js', () => { it('can search for random strs', async () => { await page.goto('http://127.0.0.1:5000'); - await page.waitForSelector('#SearchBar', { timeout: 10000 }); - await page.waitForSelector('#SearchButton', { timeout: 10000 }); + await page.waitForSelector('#SearchBar'); await page.type('#SearchBar > div > input', 'asdf'); - await page.click('#SearchButton'); - await page.waitForSelector('.content', { timeout: 10000 }); + await page.click('#SearchBar > div > button'); + await page.waitForSelector('.content'); const text = await page.$eval('.content', (e) => e.textContent); expect(text).toContain('No results match your search term'); }); it('can search for cryptography using the free text method and it returns both Nodes and CRES', async () => { await page.goto('http://127.0.0.1:5000'); - await page.waitForSelector('#SearchBar', { timeout: 10000 }); - await page.waitForSelector('#SearchButton', { timeout: 10000 }); + await page.waitForSelector('#SearchBar'); await page.type('#SearchBar > div > input', 'crypto'); - await page.click('#SearchButton'); - await page.waitForSelector('.content', { timeout: 10000 }); - await page.waitForSelector('.standard-page__links-container', { timeout: 10000 }); + await page.click('#SearchBar > div > button'); + await page.waitForSelector('.content'); const text = await page.$eval('.content', (e) => e.textContent); expect(text).not.toContain('No results match your search term'); - + + await page.waitForSelector('.standard-page__links-container'); const results = await page.$$('.standard-page__links-container'); expect(results.length).toBeGreaterThan(1); @@ -57,11 +56,12 @@ describe('App.js', () => { it('can search for a standard by name, section and the standard page works as expected', async () => { await page.goto('http://127.0.0.1:5000/node/standard/ASVS'); - await page.waitForSelector('.content', { timeout: 10000 }); - await page.waitForSelector('.standard-page__links-container', { timeout: 10000 }); + await page.waitForSelector('.content'); const text = await page.$$('.content', (e) => e.textContent); expect(text).not.toContain('No results match your search term'); + await page.waitForSelector('.standard-page__links-container'); + // title match const page_title = await page.$eval('.standard-page__heading', (e) => e.textContent); expect(page_title).toContain('ASVS'); @@ -73,12 +73,12 @@ describe('App.js', () => { // pagination const original_content = await page.content(); await page.click('a[type="pageItem"][value="2"]'); - await page.waitForSelector('.content', { timeout: 10000 }); + await page.waitForSelector('.content'); expect(await page.content()).not.toEqual(original_content); // link to section await page.click('.standard-page__links-container>.title>a'); - await page.waitForSelector('.content', { timeout: 10000 }); + await page.waitForSelector('.content'); const url = await page.url(); expect(url).toContain('section'); const section = await page.$eval('.standard-page > span:nth-child(2)', (e) => e.textContent); @@ -103,14 +103,14 @@ describe('App.js', () => { it('can search for a cre', async () => { await page.goto('http://127.0.0.1:5000'); - await page.waitForSelector('#SearchBar', { timeout: 10000 }); - await page.waitForSelector('#SearchButton', { timeout: 10000 }); + await page.waitForSelector('#SearchBar'); await page.type('#SearchBar > div > input', '558-807'); - await page.click('#SearchButton'); - await page.waitForSelector('.content', { timeout: 10000 }); - await page.waitForSelector('.standard-page__links-container', { timeout: 10000 }); + await page.click('#SearchBar > div > button'); + await page.waitForSelector('.content'); const text = await page.$$('.content', (e) => e.textContent); expect(text).not.toContain('No results match your search term'); + + await page.waitForSelector('.standard-page__links-container'); // title match const entry_title = await page.$eval('div.title.document-node', (e) => e.textContent); @@ -124,7 +124,7 @@ describe('App.js', () => { await page.click('.dropdown'); const selector = '.standard-page__links-container>.document-node>.document-node__link-type-container:nth-child(2)'; - await page.waitForSelector(selector, { timeout: 10000 }); + await page.waitForSelector(selector); const nested = await page.$$( '.standard-page__links-container>.document-node>.document-node__link-type-container>div>.accordion' @@ -134,7 +134,7 @@ describe('App.js', () => { it('can filter', async () => { await page.goto('http://127.0.0.1:5000/cre/558-807?applyFilters=true&filters=asvs'); - await page.waitForSelector('.cre-page__links-container', { timeout: 10000 }); + await page.waitForSelector('.cre-page__links-container'); // Get inner text const innerText = await page.evaluate( () => (document.querySelector('.cre-page__links-container') as HTMLElement)?.innerText @@ -145,7 +145,7 @@ describe('App.js', () => { // ensure case insensitive filtering await page.goto('http://127.0.0.1:5000/cre/558-807?applyFilters=true&filters=ASVS'); - await page.waitForSelector('.cre-page__links-container', { timeout: 10000 }); + await page.waitForSelector('.cre-page__links-container'); const intxt = await page.evaluate( () => (document.querySelector('.cre-page__links-container') as HTMLElement)?.innerText ); @@ -164,7 +164,6 @@ describe('App.js', () => { expect(response.url()).toBe('http://127.0.0.1:5000/node/standard/CWE/sectionid/1002'); const redirectResponse = await page.goto('http://127.0.0.1:5000/smartlink/standard/CWE/404'); - page.waitForNavigation('networkidle2'); expect(redirectResponse.url()).toBe('https://cwe.mitre.org/data/definitions/404.html'); }); diff --git a/jest.config.js b/jest.config.js deleted file mode 100755 index c005b3e3c..000000000 --- a/jest.config.js +++ /dev/null @@ -1,72 +0,0 @@ -module.exports = { - // All imported modules in your tests should be mocked automatically - // automock: false, - - // Automatically clear mock calls, instances and results before every test - clearMocks: true, - // Indicates whether the coverage information should be collected while executing the test - collectCoverage: true, - - // An array of glob patterns indicating a set of files for which coverage information should be collected - collectCoverageFrom: ['*.ts'], - - // The directory where Jest should output its coverage files - coverageDirectory: 'coverage', - - // An array of regexp pattern strings used to skip coverage collection - coveragePathIgnorePatterns: ['/node_modules/'], - - // Indicates which provider should be used to instrument code for coverage - coverageProvider: 'babel', - - // A list of reporter names that Jest uses when writing coverage reports - coverageReporters: [ - 'json', - // "text", - // "lcov", - // "clover" - ], - - // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers. - maxWorkers: '50%', - - // An array of directory names to be searched recursively up from the requiring module's location - moduleDirectories: ['node_modules', 'src'], - - // An array of file extensions your modules use - moduleFileExtensions: [ - 'js', - 'jsx', - 'ts', - 'tsx', - // "json", - // "node" - ], - - // Activates notifications for test results - notify: false, - - // An enum that specifies notification mode. Requires { notify: true } - // notifyMode: "failure-change", - - // Automatically reset mock state before every test - resetMocks: false, - - // The root directory that Jest should scan for tests and modules within - rootDir: 'application/frontend/src/', - - // A list of paths to directories that Jest should use to search for files in - roots: [], - - // The number of seconds after which a test is considered as slow and reported as such in the results. - slowTestThreshold: 5, - - // The test environment that will be used for testing - testEnvironment: 'jsdom', - - // The glob patterns Jest uses to detect test files - testMatch: ['**/test/*.ts', 'basic-e2etests.ts'], - - // Indicates whether each individual test should be reported during the run - verbose: true, -}; diff --git a/package.json b/package.json index 3b66e89fd..cf338da93 100755 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "lint": "prettier --write '**/*.tsx' '**/*.ts' '**/*.js'", "build": "webpack --config webpack.prod.js", "start": "webpack serve", - "test:e2e": "jest -c jest.config.js --testPathPattern=.*-e2etest.tsx?$", + "test:e2e": "jest", "postinstall": "del-cli node_modules/@types/react-dom/node_modules/@types && del-cli node_modules/webpack/types.d.ts" }, "keywords": [],