From fa935e7740751cff0e54757a0f9cdc11294a31cf Mon Sep 17 00:00:00 2001 From: Joaquim Neto Date: Fri, 23 Apr 2021 00:39:41 -0300 Subject: [PATCH] =?UTF-8?q?Melhoria=20nas=20valida=C3=A7=C3=B5es=20e=20nov?= =?UTF-8?q?os=20testes=20(#28)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: :bug: Modificações realizadas nas funções validate(), checkErrorsPerSchema(), checkMissingProperty(), revalidateSchema() - validate():Criei uma variavel booleana(objTreated) para auxiliar na validação dos objetos. - checkErrorsPerSchema():Criei uma variavel booleana(itemTreated) que verifica se aquele item do objeto já teve seu erro tratado. - checkMissingProperty():Utiliza a variavel objTreated para verificar se aquele objeto já teve seu erro tratado. - revalidateSchema():Implementei uma verificação que valida se aquele objeto já teve seu erro tratado e então defini o valor da variavel objTreated. * perf: :zap: Implementado novos testes unitários e melhorias nos logs Adicionado testes caixa preta para a funcão validate Co-authored-by: Gabriel Tellaroli Ramos --- .github/workflows/codacy-analysis.yml | 6 +- .github/workflows/format-lint.yml | 3 +- .github/workflows/test.yml | 1 + datalayer-validation-core.js | 82 ++++++--- docs/functions.md | 151 ++++++++++++++++ package.json | 2 +- test/unit/datalayer-validation-core.test.js | 39 ++++- test/unit/mock-datalayer-ecommerce.json | 64 +++++++ test/unit/mock-datalayer-global.json | 28 +++ test/unit/schema-ecommerce.json | 184 ++++++++++++++++++++ test/unit/schema-global.json | 181 +++++++++++++++++++ 11 files changed, 709 insertions(+), 32 deletions(-) create mode 100644 docs/functions.md create mode 100644 test/unit/mock-datalayer-ecommerce.json create mode 100644 test/unit/mock-datalayer-global.json create mode 100644 test/unit/schema-ecommerce.json create mode 100644 test/unit/schema-global.json diff --git a/.github/workflows/codacy-analysis.yml b/.github/workflows/codacy-analysis.yml index 01fa0b2..7e62010 100644 --- a/.github/workflows/codacy-analysis.yml +++ b/.github/workflows/codacy-analysis.yml @@ -6,13 +6,13 @@ # For more information on Codacy Analysis CLI in general, see # https://github.com/codacy/codacy-analysis-cli. -name: Codacy Security Scan +name: Codacy on: ['push'] jobs: codacy-security-scan: - name: Codacy Security Scan + name: Codacy Analysis runs-on: ubuntu-latest steps: # Checkout the repository to the GitHub Actions runner @@ -21,7 +21,7 @@ jobs: # Execute Codacy Analysis CLI and generate a SARIF output with the security issues identified during the analysis - name: Run Codacy Analysis CLI - uses: codacy/codacy-analysis-cli-action@2.0.1 + uses: codacy/codacy-analysis-cli-action@3.0.0 with: # Check https://github.com/codacy/codacy-analysis-cli#project-token to get your project token from your Codacy repository # You can also omit the token and run the tools that support default configurations diff --git a/.github/workflows/format-lint.yml b/.github/workflows/format-lint.yml index 8823fd3..e36df51 100644 --- a/.github/workflows/format-lint.yml +++ b/.github/workflows/format-lint.yml @@ -2,6 +2,7 @@ name: Lint and format on: ['push'] jobs: format: + name: Lint and format runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 @@ -19,7 +20,7 @@ jobs: npm ci npm run format - name: Commit changes - uses: stefanzweifel/git-auto-commit-action@v4.1.2 + uses: stefanzweifel/git-auto-commit-action@v4.10.0 with: commit_message: 'style: :lipstick: Apply formatting changes' branch: ${{ github.head_ref }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 68b35da..8aa22de 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,6 +2,7 @@ name: Test on: [push, pull_request] jobs: run: + name: Test runs-on: ubuntu-latest steps: - name: Checkout diff --git a/datalayer-validation-core.js b/datalayer-validation-core.js index 3342434..2ac42b1 100755 --- a/datalayer-validation-core.js +++ b/datalayer-validation-core.js @@ -1,7 +1,10 @@ const schemaParser = require('./schema-parser'); const Ajv = require('ajv'); -const debugging = process.env.PENGUIN_DEBUGGING || false; +const debugging = process.env.PENGUIN_DEBUGGING; let fullValidation = []; +let objTreated; +let itemTreated; +let partialError = { occurrences: 0, trace: '' }; const ajv = new Ajv({ schemaId: 'auto', @@ -26,6 +29,7 @@ function validationResult(status, message, dlObject, objectName, keyName) { dataLayerObject: dlObject, objectName: objectName, keyName: keyName, + partialError: partialError, }); } @@ -58,8 +62,8 @@ function checkValidEvent(schemaItem, dataLayer) { */ function revalidateSchema(shadowSchema, errorMessage, dataLayer, schemaIndex, schemaArray, dlObj) { let tempObj = JSON.parse(JSON.stringify(dataLayer)); - let innerSchema = JSON.parse(JSON.stringify(shadowSchema)); //ajustei o innerSchema pra receber o objeto como uma nova instância, e não por referência - let verify_required = Object.keys(innerSchema).indexOf('required'); //Verifica se existe required dentro do innerSchema + let innerSchema = JSON.parse(JSON.stringify(shadowSchema)); + let verify_required = Object.keys(innerSchema).indexOf('required'); if (verify_required == -1) { let found = innerSchema.contains.required.indexOf(errorMessage.params.missingProperty); @@ -67,23 +71,22 @@ function revalidateSchema(shadowSchema, errorMessage, dataLayer, schemaIndex, sc if (found > -1) { dlObjProperty = errorMessage.params.missingProperty; - innerSchema.contains.required = innerSchema.contains.required.filter((keyword) => keyword === dlObjProperty); //Então agora ele passa a remover do required todas as propriedades que não são iguais à que está dentro do tempObj + innerSchema.contains.required = innerSchema.contains.required.filter((keyword) => keyword === dlObjProperty); for (prop in innerSchema.contains.properties) { if (prop !== dlObjProperty) { delete innerSchema.contains.properties[prop]; - } //e faz o mesmo com as propriedades do schema pra igualar e deixar ele somente com o que precisa ser validado + } } let isInnerSchemaEmpty = - Object.entries(innerSchema.contains.properties).length === 0 && dataLayer.constructor === Object; //um safe check pra garantir que o objeto não ficou vazio + Object.entries(innerSchema.contains.properties).length === 0 && dataLayer.constructor === Object; if ( innerSchema.contains.required.length > 0 && !isInnerSchemaEmpty && - /*ajv.validate(innerSchema, tempObj) &&*/ Object.keys(innerSchema.contains.properties)[0] !== 'event' + Object.keys(innerSchema.contains.properties)[0] !== 'event' ) { - //essa validação tava cagada pq ele tava validando o event no nível de base e fodendo com a porra toda. Isso ainda pode ser um problema mais pra frente se alguém validationResult( 'ERROR', `Hit sent without the following property: ${errorMessage.params.missingProperty}`, @@ -91,8 +94,22 @@ function revalidateSchema(shadowSchema, errorMessage, dataLayer, schemaIndex, sc '', errorMessage.params.missingProperty ); - if (errorMessage.dataPath.indexOf(Object.keys(schemaArray[schemaIndex].properties)[1]) > -1) { - schemaArray.splice(schemaIndex, 1); + try { + let schemaItemKeys = Object.keys(schemaArray[schemaIndex].properties); + if (errorMessage.dataPath.indexOf(schemaItemKeys[1]) > -1) { + if (errorMessage.dataPath.indexOf('[0]') > -1) { + schemaArray.splice(schemaIndex, 1); + objTreated = true; + } + } + } catch (e) { + if (schemaArray[schemaIndex] == undefined) { + partialError.occurrences++; + partialError.trace = e; + trace(e); + } else { + trace('Objeto ' + errorMessage.dataPath + ' já teve seu erro tratado!!'); + } } } } else { @@ -109,7 +126,7 @@ function revalidateSchema(shadowSchema, errorMessage, dataLayer, schemaIndex, sc } } } else { - let found = innerSchema.required.indexOf(errorMessage.params.missingProperty); //ainda mantive esse laço que checa se o schema interno tem a propriedade descrita na mensagem de erro filtrada + let found = innerSchema.required.indexOf(errorMessage.params.missingProperty); if (found > -1) { //e caso o valor seja encontrado @@ -118,21 +135,20 @@ function revalidateSchema(shadowSchema, errorMessage, dataLayer, schemaIndex, sc } else { dlObjProperty = Object.keys(tempObj)[0]; } - innerSchema.required = innerSchema.required.filter((keyword) => keyword === dlObjProperty); //Então agora ele passa a remover do required todas as propriedades que não são iguais à que está dentro do tempObj + innerSchema.required = innerSchema.required.filter((keyword) => keyword === dlObjProperty); for (prop in innerSchema.properties) { if (prop !== dlObjProperty) { delete innerSchema.properties[prop]; - } //e faz o mesmo com as propriedades do schema pra igualar e deixar ele somente com o que precisa ser validado + } } - let isInnerSchemaEmpty = Object.entries(innerSchema.properties).length === 0 && dataLayer.constructor === Object; //um safe check pra garantir que o objeto não ficou vazio + let isInnerSchemaEmpty = Object.entries(innerSchema.properties).length === 0 && dataLayer.constructor === Object; if ( innerSchema.required.length > 0 && !isInnerSchemaEmpty && - /*ajv.validate(innerSchema, tempObj) &&*/ Object.keys(innerSchema.properties)[0] !== 'event' + Object.keys(innerSchema.properties)[0] !== 'event' ) { - //essa validação tava cagada pq ele tava validando o event no nível de base e fodendo com a porra toda. Isso ainda pode ser um problema mais pra frente se alguém validationResult( 'ERROR', `Hit "${errorMessage.dataPath}" sent without the following property: ${errorMessage.params.missingProperty}`, @@ -140,12 +156,20 @@ function revalidateSchema(shadowSchema, errorMessage, dataLayer, schemaIndex, sc errorMessage.dataPath, errorMessage.params.missingProperty ); - // TODO: Avaliar melhor essa lógica - if ( - schemaIndex < schemaArray.length && - errorMessage.dataPath.indexOf(Object.keys(schemaArray[schemaIndex].properties)[1]) > -1 - ) { - schemaArray.splice(schemaIndex, 1); + try { + let schemaItemKeys = Object.keys(schemaArray[schemaIndex].properties); + if (errorMessage.dataPath.indexOf(schemaItemKeys[1]) > -1) { + schemaArray.splice(schemaIndex, 1); + objTreated = true; + } + } catch (e) { + if (schemaArray[schemaIndex] == undefined) { + partialError.occurrences++; + partialError.trace = e; + trace(e); + } else { + trace('Objeto ' + errorMessage.dataPath + ' já teve seu erro tratado!!'); + } } } } else { @@ -175,7 +199,7 @@ function checkMissingProperty(schemaItem, dataLayer) { let errors = ajv.errors; trace(`retorno ajv ${JSON.stringify(array)}`); - if (!valid) { + if (!valid && !objTreated) { errors .filter((error) => error.schema.constructor === Object && error.keyword === 'required') .map((eachError) => { @@ -206,10 +230,13 @@ function checkMissingProperty(schemaItem, dataLayer) { * @param {*} dataLayer */ function checkErrorsPerSchema(schemaItem, dataLayer) { + itemTreated = false; schemaItem.forEach((item, index) => { let valid = ajv.validate(item, dataLayer); let errors = ajv.errors; + if (itemTreated) return; + if (!valid && item.required[1] == Object.keys(dataLayer)[1]) { errors .filter((error) => { @@ -254,6 +281,7 @@ function checkErrorsPerSchema(schemaItem, dataLayer) { } }); schemaItem.splice(index, 1); + itemTreated = true; } }); } @@ -281,12 +309,16 @@ let validate = (schema, dataLayer, callback) => { let schemaItem = schema.array.items; let isSchemaEmpty = schemaItem.length === 0; let isObjEmpty = Object.entries(dataLayer).length === 0 && dataLayer.constructor === Object; + objTreated = false; + itemTreated = false; if (isSchemaEmpty) { validationResult('ERROR', `No more items to validate`, JSON.stringify(dataLayer)); } else if (!checkValidEvent(schemaItem, dataLayer) && !isObjEmpty) { checkMissingProperty(schemaItem, dataLayer); - checkErrorsPerSchema(schemaItem, dataLayer); + if (!objTreated) { + checkErrorsPerSchema(schemaItem, dataLayer); + } } else if (isObjEmpty) { checkMissingEvents(schemaItem, dataLayer); } @@ -300,7 +332,7 @@ let validate = (schema, dataLayer, callback) => { * @param {Object} log Que será apresentado no stdout */ function trace(log) { - if (debugging) { + if (debugging === 'true') { console.log(log); } } diff --git a/docs/functions.md b/docs/functions.md new file mode 100644 index 0000000..2f6819f --- /dev/null +++ b/docs/functions.md @@ -0,0 +1,151 @@ +## Functions + +
+
validationResult(status, message, dlObject, objectName, keyName)
+
+
checkValidEvent(schemaItem, dataLayer)
+
+
revalidateSchema(shadowSchema, errorMessage, dataLayer, schemaIndex, schemaArray, dlObj)
+
+
checkMissingProperty(schemaItem, dataLayer)
+
+
checkErrorsPerSchema(schemaItem, dataLayer)
+
+
checkMissingEvents(schemaItem, dataLayer)
+
+
validate(schema, dataLayer, callback)
+

Valida as chaves da camada de dados com base no schema informado

+
+
trace(log)
+

Enviado o log para o stdout, se somente se, a variável debugging = true

+
+
parseEvent(event)
+

Converte a chave para objeto de verificação

+
+
parseToDataLayer(items)
+

Converte a regra de validação para a respesentação da camada de dados.

+
+
+ + + +## validationResult(status, message, dlObject, objectName, keyName) + +**Kind**: global function + +| Param | Type | +| ---------- | --------------- | +| status | \* | +| message | \* | +| dlObject | \* | +| objectName | \* | +| keyName | \* | + + + +## checkValidEvent(schemaItem, dataLayer) + +**Kind**: global function + +| Param | Type | +| ---------- | --------------- | +| schemaItem | \* | +| dataLayer | \* | + + + +## revalidateSchema(shadowSchema, errorMessage, dataLayer, schemaIndex, schemaArray, dlObj) + +**Kind**: global function + +| Param | Type | +| ------------ | --------------- | +| shadowSchema | \* | +| errorMessage | \* | +| dataLayer | \* | +| schemaIndex | \* | +| schemaArray | \* | +| dlObj | \* | + + + +## checkMissingProperty(schemaItem, dataLayer) + +**Kind**: global function + +| Param | Type | +| ---------- | --------------- | +| schemaItem | \* | +| dataLayer | \* | + + + +## checkErrorsPerSchema(schemaItem, dataLayer) + +**Kind**: global function + +| Param | Type | +| ---------- | --------------- | +| schemaItem | \* | +| dataLayer | \* | + + + +## checkMissingEvents(schemaItem, dataLayer) + +**Kind**: global function + +| Param | Type | +| ---------- | --------------- | +| schemaItem | \* | +| dataLayer | \* | + + + +## validate(schema, dataLayer, callback) + +Valida as chaves da camada de dados com base no schema informado + +**Kind**: global function + +| Param | Type | Description | +| --------- | ------------------- | ---------------------------------------------------------------------------------------------------------- | +| schema | object | Json com as regras de validação da camada | +| dataLayer | object | Json com as chaves da camada de dados | +| callback | \* | Função que será executada após o sucesso da validação, como parâmetro um array com o status das validações | + + + +## trace(log) + +Enviado o log para o stdout, se somente se, a variável debugging = true + +**Kind**: global function + +| Param | Type | Description | +| ----- | ------------------- | ------------------------------ | +| log | Object | Que será apresentado no stdout | + + + +## parseEvent(event) + +Converte a chave para objeto de verificação + +**Kind**: global function + +| Param | Type | +| ----- | ------------------- | +| event | Object | + + + +## parseToDataLayer(items) + +Converte a regra de validação para a respesentação da camada de dados. + +**Kind**: global function + +| Param | Type | +| ----- | ------------------- | +| items | Object | diff --git a/package.json b/package.json index 7eca3ba..f2b9bf2 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "scripts": { "format": "prettier --write .", "lint-md": "remark .", - "lint-prettier": "npx prettier --check .", + "lint-prettier": "npx prettier --check . || exit 0", "lint": "npm run lint-md && npm run lint-prettier", "unit-test": "mocha ./test/unit -timeout 8000", "all-test": "npm run unit-test", diff --git a/test/unit/datalayer-validation-core.test.js b/test/unit/datalayer-validation-core.test.js index 11e1efd..3668c9f 100644 --- a/test/unit/datalayer-validation-core.test.js +++ b/test/unit/datalayer-validation-core.test.js @@ -10,15 +10,50 @@ afterEach(): It’s a hook to run after each it() or describe(); const { assert } = require('chai'); const chai = require('chai'); const expect = chai.expect; -const sinon = require('sinon'); +const fs = require('fs'); -const core = require('./../../datalayer-validation-core'); +let core; +const schemaEcommerce = JSON.parse(fs.readFileSync('test/unit/schema-ecommerce.json').toString()); +const mockDatalayerEcommerce = JSON.parse(fs.readFileSync('test/unit/mock-datalayer-ecommerce.json').toString()); +const schemaGlobal = JSON.parse(fs.readFileSync('test/unit/schema-global.json').toString()); +const mockDatalayerGlobal = JSON.parse(fs.readFileSync('test/unit/mock-datalayer-global.json').toString()); describe('datalayer-validation-core', () => { + beforeEach(() => { + core = require('./../../datalayer-validation-core'); + }); describe('#validate()', () => { it('Deve ser uma function', () => { assert.isFunction(core.validate); }); + + it('Deve validar array de produtos', () => { + let result = []; + mockDatalayerEcommerce.forEach((eventoDataLayer) => { + result = result.concat(core.validate(schemaEcommerce, eventoDataLayer, (i) => {})); + }); + expect(result).to.be.an('array').that.not.empty; + expect(result).to.be.an('array').that.have.lengthOf(13); + expect(result[12].partialError.occurrences).to.be.equal(10); + expect(result[0].message).to.be.equal('Hit sent without the following property: nomeLojista'); + expect(result[0].status).to.be.equal('ERROR'); + }); + it('Deve validar datalayer com dois eventos de sucesso e um erro', () => { + let result = []; + mockDatalayerGlobal.forEach((eventoDataLayer) => { + result = result.concat(core.validate(schemaGlobal, eventoDataLayer, (i) => {})); + }); + + expect(result).to.be.an('array').that.not.empty; + expect(result).to.be.an('array').that.have.lengthOf(3); + expect(result[0].partialError.occurrences).to.be.equal(10); + expect(result[0].message).to.be.equal('Validated Successfully'); + expect(result[0].status).to.be.equal('OK'); + expect(result[1].message).to.be.equal('Validated Successfully'); + expect(result[1].status).to.be.equal('OK'); + expect(result[2].message).to.be.equal('Hit ".pagina" sent without the following property: navegacao'); + expect(result[2].status).to.be.equal('ERROR'); + }); }); describe('#validationResult()', () => { diff --git a/test/unit/mock-datalayer-ecommerce.json b/test/unit/mock-datalayer-ecommerce.json new file mode 100644 index 0000000..71d196b --- /dev/null +++ b/test/unit/mock-datalayer-ecommerce.json @@ -0,0 +1,64 @@ +[ + { + "event": "checkout", + "ecommerce": { + "checkout": { + "etapa": 1, + "tipoFrete": "normal", + "prazoEntrega": 4, + "cep": "1010000", + "valorFreteTotal": 0, + "faixaFrete": "0", + "tipoVendedor": "b2c", + "quantidadeTotal": 2, + "produtos": [ + { + "sku": "55014765", + "idProduto": "17750149", + "nome": "iphone 11 apple 64gb branco tela de 61 camera dupla de 12mp ios", + "idLojista": "10037", + "tipoVendedor": "b2c", + "preco": "4299", + "idDepartamento": "38", + "nomeDepartamento": "telefones e celulares", + "idMarca": "19", + "nomeMarca": "apple", + "quantidade": 1, + "tipoFrete": "normal", + "prazoEntrega": 4, + "valorFrete": 0 + }, + { + "sku": "13319410", + "idProduto": "9741702", + "nome": "ferro a vapor oster 5907 com antiaderente dourado e branco", + "idLojista": "10037", + "tipoVendedor": "b2c", + "preco": 89.99, + "idDepartamento": "73", + "nomeDepartamento": "eletroportateis", + "idMarca": "864", + "nomeMarca": "oster", + "variacao": "110v", + "quantidade": 1, + "tipoFrete": "normal", + "prazoEntrega": 4, + "valorFrete": 0 + } + ] + } + }, + "gtm.uniqueEventId": 28 + }, + { "event": "gtm.dom", "gtm.uniqueEventId": 33 }, + { + "event": "dcm_checkout", + "dcm": { + "sku": "55014765", + "nome": "iphone 11 apple 64gb branco tela de 61 camera dupla de 12mp ios", + "idDepartamento": "38", + "preco": 4299 + }, + "gtm.uniqueEventId": 151 + } +] diff --git a/test/unit/mock-datalayer-global.json b/test/unit/mock-datalayer-global.json new file mode 100644 index 0000000..42dbd4b --- /dev/null +++ b/test/unit/mock-datalayer-global.json @@ -0,0 +1,28 @@ +[ + { "gtm.start": 1619146085504, "event": "gtm.js", "gtm.uniqueEventId": 1 }, + { + "event": "update", + "aplicacao": { + "bandeira": "br", + "dominio": "dp6.com.br", + "ambiente": "producao", + "device": "desktop", + "servidor": "01" + }, + "gtm.uniqueEventId": 2 + }, + { "event": "update", "usuario": { "statusLogin": "visitante", "idUsuario": "12345" }, "gtm.uniqueEventId": 3 }, + { + "event": "update", + "pagina": { + "url": "https://www.dp6.com.br", + "nomePagina": "/vitrine/home", + "templatePagina": "home", + "tituloPagina": "dp6" + }, + "gtm.uniqueEventId": 4 + }, + { "event": "gtm.js", "gtm.uniqueEventId": 5 }, + { "event": "gtm.dom", "gtm.uniqueEventId": 6 }, + { "event": "gtm.load", "gtm.uniqueEventId": 7 } +] diff --git a/test/unit/schema-ecommerce.json b/test/unit/schema-ecommerce.json new file mode 100644 index 0000000..9053caf --- /dev/null +++ b/test/unit/schema-ecommerce.json @@ -0,0 +1,184 @@ +{ + "$schema": "", + "title": "Checkout - Etapa 1", + "array": { + "$id": "#/properties/schema", + "type": "array", + "items": [ + { + "type": "object", + "properties": { + "event": { + "type": "string", + "enum": ["checkout"] + }, + "ecommerce": { + "type": "object", + "properties": { + "checkout": { + "type": "object", + "properties": { + "etapa": { + "type": "number", + "enum": ["1", 1] + }, + "tipoFrete": { + "type": "string", + "pattern": "normal|retira rapido|expressa|agendada" + }, + "prazoEntrega": { + "type": "number" + }, + "cep": { + "type": "string", + "pattern": "[0-9]{5}" + }, + "valorFreteTotal": { + "type": "number" + }, + "faixaFrete": { + "type": "string", + "enum": [null] + }, + "tipoVendedor": { + "type": "string", + "pattern": "b2c|marketplace|misto" + }, + "quantidadeTotal": { + "type": "number" + }, + "cupom": { + "type": "string", + "enum": [null] + }, + "produtos": { + "type": "array", + "minItems": 1, + "contains": { + "type": "object", + "properties": { + "sku": { + "type": "string", + "pattern": ".*" + }, + "idProduto": { + "type": "string", + "pattern": ".*" + }, + "nome": { + "type": "string", + "pattern": ".*" + }, + "idLojista": { + "type": "string", + "pattern": ".*" + }, + "nomeLojista": { + "type": "string", + "pattern": ".*" + }, + "tipoVendedor": { + "type": "string", + "pattern": "b2c|marketplace|misto" + }, + "preco": { + "type": "number" + }, + "idDepartamento": { + "type": "string", + "pattern": ".*" + }, + "nomeDepartamento": { + "type": "string", + "pattern": ".*" + }, + "idLinha": { + "type": "string", + "pattern": ".*" + }, + "nomeLinha": { + "type": "string", + "pattern": ".*" + }, + "idSubLinha": { + "type": "string", + "pattern": ".*" + }, + "nomeSubLinha": { + "type": "string", + "pattern": ".*" + }, + "idMarca": { + "type": "string", + "pattern": ".*" + }, + "nomeMarca": { + "type": "string", + "pattern": ".*" + }, + "variacao": { + "type": "string", + "pattern": ".*" + }, + "quantidade": { + "type": "number" + }, + "tipoFrete": { + "type": "string", + "pattern": "normal|retira rapido|expressa|agendada" + }, + "prazoEntrega": { + "type": "number" + }, + "valorFrete": { + "type": "number" + }, + "planoGarantiaEstendida": { + "type": "number" + }, + "tipoOferta": { + "type": "string" + } + }, + "required": [ + "sku", + "idProduto", + "nome", + "idLojista", + "nomeLojista", + "preco", + "idDepartamento", + "nomeDepartamento", + "idLinha", + "nomeLinha", + "idSubLinha", + "nomeSubLinha", + "idMarca", + "nomeMarca", + "variacao", + "quantidade", + "tipoVendedor" + ] + } + } + }, + "required": [ + "etapa", + "tipoFrete", + "prazoEntrega", + "valorFreteTotal", + "faixaFrete", + "tipoVendedor", + "quantidadeTotal", + "produtos" + ] + } + }, + "required": ["checkout"] + } + }, + "required": ["event", "ecommerce"] + } + ] + } +} diff --git a/test/unit/schema-global.json b/test/unit/schema-global.json new file mode 100644 index 0000000..0b4fd67 --- /dev/null +++ b/test/unit/schema-global.json @@ -0,0 +1,181 @@ +{ + "$schema": "", + "title": "The Root Schema", + "array": { + "$id": "#/properties/schema", + "type": "array", + "items": [ + { + "type": "object", + "properties": { + "event": { + "type": "string", + "enum": ["update"] + }, + "aplicacao": { + "type": "object", + "properties": { + "bandeira": { + "type": "string", + "pattern": ".*" + }, + "dominio": { + "type": "string", + "pattern": "dp6.com.br" + }, + "ambiente": { + "type": "string", + "enum": ["producao"] + }, + "device": { + "type": "string", + "pattern": "desktop|mobile|msite" + }, + "servidor": { + "type": "string", + "pattern": ".*" + } + }, + "required": ["bandeira", "dominio", "ambiente", "device"] + } + }, + "required": ["event", "aplicacao"] + }, + { + "type": "object", + "properties": { + "event": { + "type": "string", + "enum": ["update"] + }, + "pagina": { + "type": "object", + "properties": { + "erro": { + "type": "string", + "pattern": ".*" + } + }, + "required": ["erro"] + } + }, + "required": ["event", "pagina"] + }, + { + "type": "object", + "properties": { + "event": { + "type": "string", + "enum": ["update"] + }, + "pagina": { + "type": "object", + "properties": { + "url": { + "type": "string", + "pattern": ".*" + }, + "nomePagina": { + "type": "string", + "pattern": ".*" + }, + "templatePagina": { + "type": "string", + "pattern": ".*" + }, + "tituloPagina": { + "type": "string", + "pattern": ".*" + }, + "erro": { + "type": "string", + "pattern": ".*" + }, + "navegacao": { + "type": "object", + "properties": { + "idDepartamento": { + "type": "string", + "pattern": ".*" + }, + "nomeDepartamento": { + "type": "string", + "pattern": ".*" + }, + "idLinha": { + "type": "string", + "pattern": ".*" + }, + "nomeLinha": { + "type": "string", + "pattern": ".*" + }, + "idSubLinha": { + "type": "string", + "pattern": ".*" + }, + "nomeSubLinha": { + "type": "string", + "pattern": ".*" + }, + "idMenu": { + "type": "string", + "pattern": ".*" + }, + "filtro": { + "type": "string", + "pattern": ".*" + }, + "origemInterna": { + "type": "string", + "pattern": ".*" + }, + "tipoOrigemInterna": { + "type": "string", + "pattern": ".*" + } + }, + "required": [ + "idDepartamento", + "nomeDepartamento", + "idLinha", + "nomeLinha", + "idSubLinha", + "nomeSubLinha", + "idMenu", + "filtro" + ] + } + }, + "required": ["url", "nomePagina", "templatePagina", "tituloPagina", "navegacao"] + } + }, + "required": ["event", "pagina"] + }, + { + "type": "object", + "properties": { + "event": { + "type": "string", + "enum": ["update"] + }, + "usuario": { + "type": "object", + "properties": { + "idUsuario": { + "type": "string", + "pattern": ".*" + }, + "statusLogin": { + "type": "string", + "pattern": "visitante" + } + }, + "required": ["statusLogin", "idUsuario"] + } + }, + "required": ["event", "usuario"] + } + ] + } +}