diff --git a/core/entities/Graph.js b/core/entities/Graph.js new file mode 100644 index 0000000..e69de29 diff --git a/core/entities/Node.js b/core/entities/Node.js new file mode 100644 index 0000000..d632419 --- /dev/null +++ b/core/entities/Node.js @@ -0,0 +1,119 @@ +class Node { + constructor (props) { + const validatePropsResponse = this._validateConstructionProps(props) + if (validatePropsResponse.status === 'ERR') { + this.isValid = false + throw validatePropsResponse + } + else { + this._assignProps(props) + } + } + + getProperties = () => { + let tables = [] + if (!Array.isArray(this.tables)) tables = [] + else if (this.tables.length === 0) tables = [] + else tables = this.tables.map(t => { + return t.getProperties() + }) + + const properties = { + id: this.id, + label: this.label, + type: this.type, + tables: tables, + isValid: this.isValid + } + + return properties + } + + importTables = tablesToImport => { + const validateTablesResponse = this._validateTables(tablesToImport) + if (validateTablesResponse.status === 'ERR') { + throw validateTablesResponse + } else { + let tables = [] + if (!Array.isArray(tablesToImport)) tables = [tablesToImport] + else tables = tablesToImport + this.tables = tables + } + } + + _assignProps = props => { + this.id = props.id + this.label = props.label + this.type = 'Node' + + if (props.tables) { + try { + const validateTablesResponse = this._validateTables(props.tables) + if (validateTablesResponse.status === 'ERR') { + throw validateTablesResponse + } else { + let tables = [] + if (!Array.isArray(props.tables)) tables = [props.tables] + else tables = props.tables + this.tables = tables + } + } catch (err) { + throw err + } + } else { + this.tables = [] + } + + this.isValid = true + } + + _validateTables = tablesToImport => { + const err = { + status: 'ERR', + error: { + label: 'Not all imported Tables are valid', + messages: [] + } + } + + let tables = [] + if (!tablesToImport) { + err.error.messages.push('No Tables imported') + return err + } else if (!Array.isArray(tablesToImport)) { + tables = [tablesToImport] + } else tables = tablesToImport + + for (let t = 0; t < tables.length; t++) { + if (!tables[t].isValid) { + err.error.messages.push(`Table[${t}] is not valid`) + } + } + + if (err.error.messages.length === 0){ + return { status: 'OK' } + } else{ + return err + } + } + + _validateConstructionProps = (props) => { + const err = { + status: 'ERR', + error: { + label: 'Error Creating Node', + messages: [] + } + } + if (!props.id) err.error.messages.push('No id on creation of Node') + if (!props.label) err.error.messages.push('No label on creation of Node') + + if (err.error.messages.length === 0){ + return { status: 'OK' } + } else{ + return err + } + } +} + +export default Node diff --git a/core/entities/Table.js b/core/entities/Table.js new file mode 100644 index 0000000..58442c2 --- /dev/null +++ b/core/entities/Table.js @@ -0,0 +1,86 @@ +class Table { + constructor (props) { + const validatePropsResponse = this._validateConstructionProps(props) + if (validatePropsResponse.status === 'ERR') { + this.isValid = false + throw validatePropsResponse + } + else { + this._assignProps(props) + } + } + + getProperties = () => { + return { + id: this.id, + label: this.label, + rows: this.rows, + type: this.type, + isValid: this.isValid + } + } + + getRows = () => this.rows + + _assignProps = props => { + this.id = props.id + this.label = props.label + this.type = 'Table' + this.isValid = true + + if (!Array.isArray(props.rows)) this.rows = [props.rows] + else this.rows = props.rows + } + + _validateConstructionProps = props => { + const err = { + status: 'ERR', + error: { + label: 'Error Creating Table', + messages: [] + } + } + + if(!props) { + err.error.messages.push('No props on creation of Table') + return err + } + if (!props.id) err.error.messages.push('No id on creation of Table') + if (!props.label) err.error.messages.push('No label on creation of Table') + + const validateRowsErrors = this._validateRows(props.rows) + if (validateRowsErrors.length > 0) { + validateRowsErrors.forEach(e => { + err.error.messages.push(e) + }) + } + + if (err.error.messages.length === 0){ + return { status: 'OK' } + } else{ + return err + } + } + + _validateRows = rowsToImport => { + let rows = [] + if (!Array.isArray(rowsToImport)) rows = [rowsToImport] + else rows = rowsToImport + + const errorMesages = [] + + if (rows.length === 0) { + errorMesages.push('No Tables imported') + } + + for (let r = 0; r < rows.length; r++) { + if (typeof rows[r] !== 'object') { + errorMesages.push(`row[${r}] is not an object`) + } + } + + return errorMesages + } +} + +export default Table diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..9e20cb7 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,14 @@ +{ + "name": "dmein", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..5114eb6 --- /dev/null +++ b/package.json @@ -0,0 +1,26 @@ +{ + "name": "dmein", + "version": "1.0.0", + "type": "module", + "description": "A web application to migrate data from various sources (i.e. files, DBs, APIs) and create visual reports on them.", + "main": "index.js", + "directories": { + "test": "tests" + }, + "scripts": { + "tests": "node ./tests/index.js" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/joshuashoemaker/dmein.git" + }, + "author": "Joshua Shoemaker", + "license": "ISC", + "bugs": { + "url": "https://github.com/joshuashoemaker/dmein/issues" + }, + "homepage": "https://github.com/joshuashoemaker/dmein#readme", + "devDependencies": { + "progress": "^2.0.3" + } +} diff --git a/tests/core/nodeTests.js b/tests/core/nodeTests.js new file mode 100644 index 0000000..da54f3e --- /dev/null +++ b/tests/core/nodeTests.js @@ -0,0 +1,108 @@ +import Node from '../../core/entities/Node.js' +import Table from '../../core/entities/Table.js' + +const input = { + id: 'ABC', + label: 'Test Node', + tables: [ + new Table({ + id: 'XYZ', + label: 'Test Table', + rows: [{ id: 'abc', data: 'row' }] + }) + ] +} + +const getNodeProperties = () => { + const expectedOutput = { + id: 'ABC', + label: 'Test Node', + type: 'Node', + tables: [ + { + id: 'XYZ', + label: 'Test Table', + rows: [{ id: 'abc', data: 'row' }], + type: 'Table', + isValid: true + } + ], + isValid: true + } + + try { + const node = new Node(input) + const nodeProps = node.getProperties() + if (JSON.stringify(nodeProps) == JSON.stringify(expectedOutput)) return true + else return false + } catch (err) { + return false + } +} + +const createNodeWithoutTables = () => { + const input = { + id: 'ABC', + label: 'Test Node', + } + + const expectedOutput = { + id: 'ABC', + label: 'Test Node', + type: 'Node', + tables: [], + isValid: true + } + + try { + const node = new Node(input) + const nodeProps = node.getProperties() + if (JSON.stringify(nodeProps) == JSON.stringify(expectedOutput)) return true + else return false + } catch (err) { + return false + } +} + +const importTables = () => { + const table = new Table({ + id: 'XYZ', + label: 'Test Table', + rows: [{ id: 'abc', data: 'row' }] + }) + + const expectedOutput = { + id: 'ABC', + label: 'Test Node', + type: 'Node', + tables: [{ + id: 'XYZ', + label: 'Test Table', + rows: [{ id: 'abc', data: 'row' }], + type: 'Table', + isValid: true + }], + isValid: true + } + + try { + const node = new Node({ + id: 'ABC', + label: 'Test Node', + }) + node.importTables(table) + const nodeProps = node.getProperties() + + if (JSON.stringify(nodeProps) == JSON.stringify(expectedOutput)) return true + else return false + } catch (err) { + console.log(err) + return false + } +} + +export default [ + { name: 'Entity | Get Node Properties', test: getNodeProperties }, + { name: 'Entity | Create Node Without Tables', test: createNodeWithoutTables }, + { name: 'Entity | Import Tables to Node', test: importTables } +] \ No newline at end of file diff --git a/tests/core/tableTests.js b/tests/core/tableTests.js new file mode 100644 index 0000000..97866a2 --- /dev/null +++ b/tests/core/tableTests.js @@ -0,0 +1,197 @@ +import Table from '../../core/entities/Table.js' + +const input = { + id: 'abc', + label: 'Test Label', + rows: [ + { id: '2345676', type: 'row', lat: 54, long: 31 }, + { id: '2345676', type: 'lh', lat: 31, long: -71.34 } + ] +} + +const getTableProperties = () => { + const expectedOutput = { + id: 'abc', + label: 'Test Label', + rows: [ + { id: '2345676', type: 'row', lat: 54, long: 31 }, + { id: '2345676', type: 'lh', lat: 31, long: -71.34 } + ], + type: 'Table', + isValid: true + } + + try { + const table = new Table(input) + const tableProperties = table.getProperties() + if (JSON.stringify(tableProperties) === JSON.stringify(expectedOutput)) return true + else return false + } catch (err) { + return err + } +} + +const getTableRows = () => { + const expectedOutput = [ + { id: '2345676', type: 'row', lat: 54, long: 31 }, + { id: '2345676', type: 'lh', lat: 31, long: -71.34 } + ] + + try { + const table = new Table(input) + const tableRows = table.getRows() + if (JSON.stringify(tableRows) === JSON.stringify(expectedOutput)) return true + else return false + } catch (err) { + return err + } +} + +const createTableWithNoProps = () => { + const expectedOutput = { + status: 'ERR', + error: { + label: 'Error Creating Table', + messages: ['No props on creation of Table'] + } + } + + try { + new Table() + return false + } catch (err) { + if (JSON.stringify(expectedOutput) === JSON.stringify(err)) return true + else return false + } +} + +const createTableWithOnlyId = () => { + const input = { id: 'abc' } + const expectedOutput = { + status: 'ERR', + error: { + label: 'Error Creating Table', + messages: [ + 'No label on creation of Table', + 'row[0] is not an object' + ] + } + } + + try { + new Table(input) + return false + } catch (err) { + if (JSON.stringify(expectedOutput) === JSON.stringify(err)) return true + else return false + } +} + +const createTableWithOnlyLabel = () => { + const input = { label: 'Test Label' } + const expectedOutput = { + status: 'ERR', + error: { + label: 'Error Creating Table', + messages: [ + 'No id on creation of Table', + 'row[0] is not an object' + ] + } + } + + try { + new Table(input) + return false + } catch (err) { + if (JSON.stringify(expectedOutput) === JSON.stringify(err)) return true + else return false + } +} + +const createTableWithEverythingButRows = () => { + const input = { id: 'abc', label: 'Test Label' } + const expectedOutput = { + status: 'ERR', + error: { + label: 'Error Creating Table', + messages: [ + 'row[0] is not an object' + ] + } + } + + try { + new Table(input) + return false + } catch (err) { + if (JSON.stringify(expectedOutput) === JSON.stringify(err)) return true + else return false + } +} + +const createTableWithInvalidRows = () => { + const input = { + id: 'abc', + label: 'Test Label', + rows: [ + { id: '2345676', type: 'lh', lat: 31, long: -71.34 }, + 'fake row' + ] + } + const expectedOutput = { + status: 'ERR', + error: { + label: 'Error Creating Table', + messages: [ + 'row[1] is not an object' + ] + } + } + + try { + new Table(input) + return false + } catch (err) { + if (JSON.stringify(expectedOutput) === JSON.stringify(err)) return true + else return false + } +} + +const createTableWithRowsAsNotArray = () => { + const input = { + id: 'abc', + label: 'Test Label', + rows: { id: '2345676', type: 'lh', lat: 31, long: -71.34 } + } + + const expectedOutput = { + id: 'abc', + label: 'Test Label', + rows: [ + { id: '2345676', type: 'lh', lat: 31, long: -71.34 } + ], + type: 'Table', + isValid: true + } + + try { + const table = new Table(input) + const tableProperties = table.getProperties() + if (JSON.stringify(tableProperties) === JSON.stringify(expectedOutput)) return true + else return false + } catch (err) { + return err + } +} + +export default [ + { name: 'Entity | Get Table Properties', test: getTableProperties }, + { name: 'Entity | Get Table Rows', test: getTableRows }, + { name: 'Entiry | Table With Invalid Props', test: createTableWithNoProps }, + { name: 'Entiry | Table With Only Id', test: createTableWithOnlyId }, + { name: 'Entiry | Table With Only Label', test: createTableWithOnlyLabel }, + { name: 'Entiry | Table With Everything But Rows', test: createTableWithEverythingButRows }, + { name: 'Entiry | Table With Invalid Rows', test: createTableWithInvalidRows }, + { name: 'Entiry | Table With Rows as Not Array', test: createTableWithRowsAsNotArray } +] \ No newline at end of file diff --git a/tests/index.js b/tests/index.js new file mode 100644 index 0000000..1c8f8f2 --- /dev/null +++ b/tests/index.js @@ -0,0 +1,43 @@ +import ProgressBar from 'progress' + +import tableTests from '../tests/core/tableTests.js' +import nodeTests from '../tests/core/nodeTests.js' + +function runTestsAndReturnFailures (tests) { + const testTotalCount = tests.length + + const testBar = new ProgressBar( + `\x1b[36mRunning Tests [:bar] :current/${testTotalCount}`, + { total: testTotalCount } + ) + + let testsFailed = [] + + for (let i = 0; i < testTotalCount; i++) { + const passedTest = tests[i].test() + testBar.tick() + + if (!passedTest) testsFailed.push(tests[i].name) + + if (testBar.complete) return testsFailed + } +} + +function init (tests) { + const failedTestsResults = runTestsAndReturnFailures(tests) + if (failedTestsResults.length === 0) { + console.log('\x1b[32m%s\x1b[0m', 'All Tests Passed!!') + } else { + console.log(`\x1b[31mFailed ${failedTestsResults.length} tests.\x1b[0m`) + failedTestsResults.forEach(test => { + console.log(`\x1b[33m${test}\x1b[0m`) + }) + } +} + +const testsArray = [ + tableTests, + nodeTests +] + +init (testsArray.flat()) \ No newline at end of file