diff --git a/.babelrc b/.babelrc
new file mode 100644
index 0000000..db7fd04
--- /dev/null
+++ b/.babelrc
@@ -0,0 +1,4 @@
+{
+ "presets": ["es2015"],
+ "plugins": ["babel-plugin-add-module-exports"]
+}
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..49ab08b
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,13 @@
+root = true
+
+[*]
+end_of_line = lf
+insert_final_newline = true
+indent_style = space
+indent_size = 4
+trim_trailing_whitespace = true
+charset = utf-8
+
+[{package.json}]
+indent_style = space
+indent_size = 2
diff --git a/.jshintrc b/.jshintrc
new file mode 100644
index 0000000..901b593
--- /dev/null
+++ b/.jshintrc
@@ -0,0 +1,9 @@
+{
+ "node": true,
+ "mocha": true,
+ "esversion": 6,
+ "globalstrict": false,
+ "asi": true,
+ "curly": true,
+ "trailing": true
+}
diff --git a/.npmignore b/.npmignore
new file mode 100644
index 0000000..2ee4664
--- /dev/null
+++ b/.npmignore
@@ -0,0 +1,5 @@
+dist
+tests
+.editorconfig
+.jshintrc
+.DS_Store
diff --git a/LICENSE.md b/LICENSE.md
index acb5a46..ea3131b 100644
--- a/LICENSE.md
+++ b/LICENSE.md
@@ -1,6 +1,6 @@
The MIT License (MIT)
-Copyright (c) 2014 Gabriel Oliveira
+Copyright (c) 2016 Gabriel Oliveira
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md
index beeec43..4ebaa9d 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,276 @@
-# rastrear-pacote
+# tracking-correios
-Módulo para consultar o status do rastreio de pacotes do Correios.
+Módulo para consulta do rastreio de pacotes do Correios. Consulta diretamento a API do Correios (SRO).
+
+## Instalação
+
+Requer `Node.js` e `npm` instalados.
+
+```sh
+$ npm install --save tracking-correios
+```
+
+Depois importe no seu código:
+
+```js
+// via require do Node.js
+const TrackingCorreios = require('tracking-correios')
+
+// ou via ES6
+import TrackingCorreios from 'tracking-correios'
+```
+
+## Consulta de eventos do código de rastreio
+
+Para consultas de um único código:
+
+```js
+// passando como string
+TrackingCorreios.track( 'DU897123996BR' )
+ .then(console.log)
+
+// ou como Array
+TrackingCorreios.track( [ 'DU897123996BR' ] )
+ .then(console.log)
+
+/*
+{
+ "numero":"DU897123996BR",
+ "sigla":"DU",
+ "nome":"ENCOMENDA E-SEDEX",
+ "categoria":"E-SEDEX",
+ "evento": [ {
+ "tipo":"BDE",
+ "status":"01",
+ "data":"12/12/2016",
+ "hora":"19:06",
+ "descricao":"Objeto entregue ao destinatário",
+ "recebedor":"",
+ "documento":"",
+ "comentario":"",
+ "local":"CEE BAURU",
+ "codigo":"17034972",
+ "cidade":"Bauru",
+ "uf":"SP"
+ }, { ... }, { ... }, { ... }
+ ]
+}
+*/
+```
+
+Exemplo de código válido, porém sem rastreio
+
+Para consultas de vários códigos simultâneos:
+
+```js
+TrackingCorreios.track([ 'DU897123996BR', 'PN273603577BR', 'DU910139445BR' ])
+ .then(console.log)
+
+/*
+[ { numero: 'DU897123996BR',
+ sigla: 'DU',
+ nome: 'ENCOMENDA E-SEDEX',
+ categoria: 'E-SEDEX',
+ evento: [ {...}, [Object], [Object], [Object], [Object], [Object] ] },
+ { numero: 'PN273603577BR',
+ sigla: 'PN',
+ nome: 'ENCOMENDA PAC (ETIQ LOGICA)',
+ categoria: 'ENCOMENDA PAC',
+ evento: [ [Object], [Object], [Object], [Object], [Object], [Object] ] },
+ { numero: 'DU910139445BR',
+ sigla: 'DU',
+ nome: 'ENCOMENDA E-SEDEX',
+ categoria: 'E-SEDEX',
+ evento: [ [Object], [Object], [Object], [Object], [Object] ] } ]
+*/
+```
+
+O método `track` validará automaticamente os objetos, removendo os inválidos:
+
+```js
+TrackingCorreios.track([ 'DU897123996BR', 'invalido' ])
+ .then(console.log)
+
+/*
+{ "numero":"DU897123996BR",
+ "sigla":"DU",
+ "nome":"ENCOMENDA E-SEDEX",
+ "categoria":"E-SEDEX",
+ "evento": [...]
+}
+*/
+```
+
+Se não tiver nenhum objeto válido a Promise rejeitará com `TrackingError`:
+
+```js
+TrackingCorreios.track('invalido')
+ .catch(console.log)
+
+/*
+{ [TrackingError: Erro ao validar os objetos.]
+ name: 'TrackingError',
+ message: 'Erro ao validar os objetos.',
+ type: 'validation_error',
+ errors:
+ [ { message: 'Nenhum objeto válido para pesquisa.',
+ service: 'objects_validation' } ] }
+*/
+```
+
+O método `track` retorna uma Promise, portanto o tratamento de erros deve ser feito pelo `.catch`. Exemplo de API fora do ar:
+
+```js
+TrackingCorreios.track('DU897123996BR')
+ .then(console.log)
+ .catch(console.log)
+
+/*
+{ [TrackingError: Erro ao se conectar ao o serviço dos Correios.]
+ name: 'TrackingError',
+ message: 'Erro ao se conectar com o serviço dos Correios.',
+ type: 'system',
+ errors:
+ [ { message: 'Ocorreu um erro ao se conectar ao serviço dos Correios: request to https://webservice.correios.com.br/service/rastro failed, reason: connect ECONNREFUSED webservice.correios.com.br',
+ service: 'service_error' } ] }
+*/
+```
+
+Pode também passar um objeto de configuração como segundo parâmetro.
+
+```js
+// Valores padrão:
+TrackingCorreios.track('DU897123996BR', {
+ username: undefined,
+ password: undefined,
+ type: "L",
+ result: "T",
+ language: "101",
+ limit: 5000
+ })
+```
+
+Os parâmetros `username`, `password`, `type`, `result` e `language` serão enviados a API dos Correios.
+
+O parâmetro `limit` indica a quantidade máxima de objetos a ser enviado por requisição. Se passar 8 mil objetos e o limite for 5 mil, o módulo fará duas requisições. Se passar mil objetos e o limite for 1, fará mil requisições.
+
+As requisições não são paralelas, serão realizadas uma após a outra. A Promise só resolverá quando todas as requisições terminarem.
+
+## Validação de objetos
+
+Esse módulo expõe três métodos auxiliares para validação de objetos.
+
+* `isValid`: verifica se o tracking é válido seguindo as regras do Correios.
+* `filter`: retorna somente os objetos válidos.
+* `validate`: retorna um objeto com os itens válidos e inválidos.
+
+Exemplos:
+
+`isValid`:
+
+```js
+TrackingCorreios.isValid('DU897123996BR')
+> true
+
+TrackingCorreios.isValid(['DU897123996BR'])
+> false
+
+TrackingCorreios.isValid('AAAAA')
+> false
+
+TrackingCorreios.isValid()
+> false
+```
+
+Verifica um único objeto por vez. Valida as iniciais também com as conhecidas do correios (veja [category](#categoria-do-objeto)). Retorna um `boolean`.
+
+------------
+
+`filter`:
+
+```js
+TrackingCorreios.filter( 'DU897123996BR' )
+> [ 'DU897123996BR' ]
+
+TrackingCorreios.filter( [ 'DU897123996BR' ] )
+> [ 'DU897123996BR' ]
+
+TrackingCorreios.filter( [ 'DU897123996BR', 'PN273603577BR', 'DU910139445BR' ] )
+> [ 'DU897123996BR', 'PN273603577BR', 'DU910139445BR' ]
+
+TrackingCorreios.filter([ 'DU897123996BR', 'invalid' ])
+> [ 'DU897123996BR' ]
+
+TrackingCorreios.filter([ 'invalid', 'AAAA' ])
+> [ ]
+
+TrackingCorreios.filter( { } )
+> [ ]
+```
+
+Sempre retornará um Array, independente se houver ou não itens válidos.
+
+------------
+
+`validate`:
+
+```js
+TrackingCorreios.validate( 'DU897123996BR' )
+> { valid: [ 'DU897123996BR' ], invalid: [ ] }
+
+TrackingCorreios.validate( [ 'DU897123996BR' ] )
+> { valid: [ 'DU897123996BR' ], invalid: [ ] }
+
+TrackingCorreios.validate( [ 'DU897123996BR', 'PN273603577BR', 'DU910139445BR' ] )
+> {
+ valid: [ 'DU897123996BR', 'PN273603577BR', 'DU910139445BR' ],
+ invalid: [ ]
+ }
+
+TrackingCorreios.validate([ 'DU897123996BR', 'invalid' ])
+> {
+ valid: [ 'DU897123996BR' ],
+ invalid: [ 'invalid' ]
+ }
+
+TrackingCorreios.validate([ 'invalid', 'AAAA' ])
+> {
+ valid: [ ],
+ invalid: [ 'invalid', 'AAAA' ]
+ }
+
+TrackingCorreios.validate( { } )
+> {
+ valid: [ ],
+ invalid: [ { } ]
+ }
+```
+
+Sempre retornará um objeto com os campos `valid` e `invalid`.
+
+## Categoria do Objeto
+
+O módulo expõe um método para retornar a categoria do objeto: `category`. Exemplo:
+
+```js
+TrackingCorreios.category('DU897123996BR')
+> 'e-SEDEX'
+
+TrackingCorreios.category(['PN273603577BR'])
+> 'PAC'
+
+TrackingCorreios.category(['AA123123123BR'])
+> undefined
+
+TrackingCorreios.category('AAAAA')
+> undefined
+
+TrackingCorreios.category()
+> undefined
+```
+
+Se não for um código de rastreio válido, retornará `undefined`.
+
+# Inspiração
+
+Arquitetura inspirada no módulo [cep-promise](https://github.com/filipedeschamps/cep-promise) de Filipe Deschamps. Queria aprender mais sobre encadeamento de Promises, funções pequenas e vários outros assuntos que ele aborda [nesse vídeo](https://www.youtube.com/watch?v=gB5Gej0O400), por isso decidi desenvolver esse módulo.
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..967a3b0
--- /dev/null
+++ b/package.json
@@ -0,0 +1,43 @@
+{
+ "name": "tracking-correios",
+ "version": "1.0.0",
+ "description": "Módulo para consulta do rastreio de pacotes do Correios",
+ "main": "dist/track.js",
+ "scripts": {
+ "test": "npm run test-unit && npm run test-e2e",
+ "test-e2e": "mocha tests/e2e/*.spec.js --timeout 15000",
+ "test-unit": "mocha tests/unit/*.spec.js",
+ "build": "babel src --out-dir dist",
+ "prepublish": "npm run build"
+ },
+ "author": "Gabriel Oliveira",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/gabrielboliveira/tracking-correios.git"
+ },
+ "bugs": {
+ "url": "https://github.com/gabrielboliveira/tracking-correios/issues"
+ },
+ "dependencies": {
+ "bluebird": "^3.4.6",
+ "isomorphic-fetch": "^2.2.1",
+ "lodash.assignin": "^4.2.0",
+ "lodash.chunk": "^4.2.0",
+ "lodash.difference": "^4.5.0",
+ "lodash.filter": "^4.6.0",
+ "lodash.flatten": "^4.4.0",
+ "lodash.get": "^4.4.2",
+ "xml2js": "^0.4.17"
+ },
+ "devDependencies": {
+ "babel-cli": "^6.18.0",
+ "babel-plugin-add-module-exports": "^0.2.1",
+ "babel-preset-es2015": "^6.18.0",
+ "chai": "^3.5.0",
+ "chai-as-promised": "^6.0.0",
+ "chai-subset": "^1.3.0",
+ "mocha": "^3.2.0",
+ "nock": "^9.0.2"
+ }
+}
diff --git a/src/errors/tracking.js b/src/errors/tracking.js
new file mode 100644
index 0000000..8896ad0
--- /dev/null
+++ b/src/errors/tracking.js
@@ -0,0 +1,11 @@
+function TrackingError (options = {}) {
+ this.name = 'TrackingError'
+ this.message = options.message
+ this.type = options.type
+ this.errors = options.errors
+}
+
+TrackingError.prototype = Object.create(Error.prototype)
+TrackingError.prototype.constructor = TrackingError
+
+module.exports = TrackingError
diff --git a/src/services/correios.js b/src/services/correios.js
new file mode 100644
index 0000000..9582462
--- /dev/null
+++ b/src/services/correios.js
@@ -0,0 +1,164 @@
+'use strict'
+
+const Promise = require("bluebird")
+
+const fetch = require('isomorphic-fetch')
+fetch.Promise = Promise
+
+const _extend = require('lodash.assignin')
+const _get = require('lodash.get')
+const _chunk = require('lodash.chunk')
+const _flatten = require('lodash.flatten')
+
+const xml2js = require('xml2js')
+
+const Helpers = require('../utils/helpers')
+
+const TrackingError = require('../errors/tracking')
+
+const parseXMLString = xml2js.parseString
+
+const url = 'https://webservice.correios.com.br/service/rastro'
+
+function fetchTracking (objects, configParams) {
+
+ return new Promise( (resolve, reject) => {
+
+ Promise.resolve(objects)
+ .then(fetchFromCorreios)
+ .then(parseFetchOutput)
+ .then(resolvePromise)
+ .catch(rejectWithError)
+
+ function fetchFromCorreios (objects) {
+ let callsChunked = _chunk(objects, configParams.limit)
+
+ return Promise.map(callsChunked, fetchFromAPI)
+ }
+
+ function fetchFromAPI (objects) {
+ const options = {
+ method: 'POST',
+ body: createSOAPEnvelope(objects),
+ mode: 'no-cors',
+ headers: {
+ 'Content-Type': 'text/xml; charset=utf-8',
+ 'cache-control': 'no-cache'
+ }
+ }
+
+ return fetch(url, options)
+ }
+
+ function parseFetchOutput(responses) {
+ return Promise.all(responses.map(parseSingleFetch))
+ .then(mergeAllResponses)
+ }
+
+ function mergeAllResponses (responses) {
+ return _flatten(responses)
+ }
+
+ function parseSingleFetch (response) {
+ if (response.ok) {
+ return response.text()
+ .then(parseXML)
+ .then(extractSuccessObject)
+ }
+
+ return response.text()
+ .then(parseXML)
+ .then(extractErrorObject)
+ .then(throwErrorObject)
+ }
+
+ function parseXML (text) {
+ return new Promise( (resolve, reject) => {
+ parseXMLString(text, (err, object) => {
+ if(!err) {
+ resolve(object)
+ } else {
+ reject(new TrackingError({
+ message: 'Não foi possível interpretar o XML de resposta.',
+ type: 'service_error',
+ errors: [{
+ message: 'Ocorreu um erro ao tratar o XML retornado pela API dos Correios.',
+ service: 'parsing_error'
+ }]
+ }))
+ }
+ })
+ })
+ }
+
+ function getSubObject (object, stringToSearch) {
+ return _get(object, stringToSearch).map(Helpers.expand)
+ }
+
+ function extractSuccessObject (object) {
+ return getSubObject(object, 'soapenv:Envelope.soapenv:Body[0].ns2:buscaEventosListaResponse[0].return[0].objeto')
+ }
+
+ function extractErrorObject (object) {
+ return getSubObject(object, 'soapenv:Envelope.soapenv:Body[0].soapenv:Fault[0].faultstring[0]')
+ }
+
+ function throwErrorObject (faultString) {
+ throw new TrackingError({
+ message: 'Erro no serviço do Correios.',
+ type: 'service_error',
+ errors: [{
+ message: `O serviço do Correios retornou o seguinte erro: ${ faultString }`,
+ service: 'service_error'
+ }]
+ })
+ }
+
+ function createSOAPEnvelope (objects) {
+ let envelope = `\n\n \n \n \n`
+
+ if(configParams.username && configParams.password) {
+ envelope += ` ${configParams.username}\n ${configParams.password}\n`
+ }
+
+ envelope += ` ${ configParams.type }\n ${ configParams.result }\n ${ configParams.language }\n`
+
+ objects.forEach((object) => {
+ envelope += ` ${ object }\n`
+ })
+
+ envelope += ` \n \n`
+ return envelope
+ }
+
+ function resolvePromise (objects) {
+ if(objects && objects.length === 1) {
+ objects = objects[0]
+ }
+ resolve(objects)
+ }
+
+ function rejectWithError (error = {}) {
+ const trackingError = new TrackingError({
+ message: error.message,
+ type: error.type,
+ errors: error.errors
+ })
+
+ if (error.name === 'FetchError') {
+ trackingError.message = 'Erro ao se conectar ao o serviço dos Correios.'
+ trackingError.errors = [{
+ message: `Ocorreu um erro ao se conectar ao serviço dos Correios: ${ error.message }`,
+ service: 'service_error'
+ }]
+ }
+
+ reject(trackingError)
+ }
+ })
+
+}
+
+module.exports = {
+ fetchTracking
+}
diff --git a/src/track.js b/src/track.js
new file mode 100644
index 0000000..ed21330
--- /dev/null
+++ b/src/track.js
@@ -0,0 +1,120 @@
+'use strict'
+
+const Promise = require("bluebird")
+
+const _filter = require('lodash.filter')
+const _extend = require('lodash.assignin')
+const _difference = require('lodash.difference')
+
+const CorreiosAPI = require('./services/correios')
+
+const TrackingError = require('./errors/tracking')
+
+const TrackingHelpers = require('./utils/tracking-helpers')
+
+const Helpers = require('./utils/helpers')
+
+const MAX_OBJECTS_CORREIOS = 5000
+
+function track (objects, configParams = {}) {
+
+ // default params
+ configParams = _extend({
+ username: undefined,
+ password: undefined,
+ type: "L",
+ result: "T",
+ language: "101",
+ limit: 5000
+ }, configParams)
+
+ return new Promise( (resolve, reject) => {
+
+ Promise.resolve( { objects, configParams } )
+ .then(validateParams)
+ .then(filterObjects)
+ .then(validateObjects)
+ .then(fetchFromCorreios)
+ .then(resolvePromise)
+ .catch(rejectWithError)
+
+ function resolvePromise (objects) {
+ resolve(objects)
+ }
+
+ function rejectWithError (error) {
+ reject(new TrackingError({
+ message: error.message,
+ type: error.type,
+ errors: error.errors
+ }))
+ }
+
+ function validateParams (params) {
+ if ( params.configParams.type && params.configParams.result &&
+ params.configParams.language && params.configParams.limit > 0 &&
+ params.configParams.limit <= MAX_OBJECTS_CORREIOS)
+ {
+ return params
+ }
+
+ throw new TrackingError({
+ message: 'Erro ao validar os parâmetros.',
+ type: 'validation_error',
+ errors: [{
+ message: 'Type, result e language não podem ser undefined.',
+ service: 'param_validation'
+ }]
+ })
+ }
+
+ function filterObjects (params) {
+ params.objects = Helpers.arrayOf(params.objects)
+
+ params.objects = filter(params.objects)
+
+ return params
+ }
+
+ function validateObjects (params) {
+ if (params.objects.length > 0) {
+ return params
+ }
+
+ throw new TrackingError({
+ message: 'Erro ao validar os objetos.',
+ type: 'validation_error',
+ errors: [{
+ message: 'Nenhum objeto válido para pesquisa.',
+ service: 'objects_validation'
+ }]
+ })
+ }
+
+ function fetchFromCorreios (params) {
+ return CorreiosAPI.fetchTracking(params.objects, params.configParams)
+ }
+ })
+}
+
+function validate (objects) {
+ objects = Helpers.arrayOf(objects)
+ let filtered = filter (objects)
+ return {
+ valid: filtered,
+ invalid: _difference(objects, filtered)
+ }
+}
+
+function filter (objects) {
+ objects = Helpers.arrayOf(objects)
+ return _filter (objects, TrackingHelpers.isValid)
+}
+
+module.exports = {
+ track,
+ validate,
+ isValid: TrackingHelpers.isValid,
+ category: TrackingHelpers.category,
+ filter
+}
diff --git a/src/utils/helpers.js b/src/utils/helpers.js
new file mode 100644
index 0000000..48a24b0
--- /dev/null
+++ b/src/utils/helpers.js
@@ -0,0 +1,31 @@
+function expand (item) {
+ if (Array.isArray(item)) {
+ if (item.length === 1) {
+ return expand(item[0])
+ }
+ return item.map(expand)
+ } else if(isObject(item)) {
+ for(let key in item) {
+ item[key] = expand(item[key])
+ }
+ } else if (isString(item)) {
+ item = item.trim()
+ }
+ return item
+}
+
+function isObject (item) {
+ return item === Object(item)
+}
+
+function isString (item) {
+ return typeof item === 'string' || item instanceof String
+}
+
+function arrayOf (item) {
+ return Array.isArray(item) ? item : ( item !== undefined ? [ item ] : [] )
+}
+
+module.exports = {
+ expand, isObject, isString, arrayOf
+}
diff --git a/src/utils/tracking-helpers.js b/src/utils/tracking-helpers.js
new file mode 100644
index 0000000..6328f8a
--- /dev/null
+++ b/src/utils/tracking-helpers.js
@@ -0,0 +1,270 @@
+const Helpers = require('./helpers')
+
+const validInitials = {
+ 'AL': 'Agentes de leitura',
+ 'AR': 'Avisos de recebimento',
+ 'AS': 'PAC - Ação Social',
+ 'CA': 'Encomenda Internacional - Colis',
+ 'CB': 'Encomenda Internacional - Colis',
+ 'CC': 'Encomenda Internacional - Colis',
+ 'CD': 'Encomenda Internacional - Colis',
+ 'CE': 'Encomenda Internacional - Colis',
+ 'CF': 'Encomenda Internacional - Colis',
+ 'CG': 'Encomenda Internacional - Colis',
+ 'CH': 'Encomenda Internacional - Colis',
+ 'CI': 'Encomenda Internacional - Colis',
+ 'CJ': 'Encomenda Internacional - Colis',
+ 'CK': 'Encomenda Internacional - Colis',
+ 'CL': 'Encomenda Internacional - Colis',
+ 'CM': 'Encomenda Internacional - Colis',
+ 'CN': 'Encomenda Internacional - Colis',
+ 'CO': 'Encomenda Internacional - Colis',
+ 'CP': 'Encomenda Internacional - Colis',
+ 'CQ': 'Encomenda Internacional - Colis',
+ 'CR': 'Carta registrada sem Valor Declarado',
+ 'CS': 'Encomenda Internacional - Colis',
+ 'CT': 'Encomenda Internacional - Colis',
+ 'CU': 'Encomenda internacional - Colis',
+ 'CV': 'Encomenda Internacional - Colis',
+ 'CW': 'Encomenda Internacional - Colis',
+ 'CX': 'Encomenda internacional - Colis ou Selo Lacre para Caixetas',
+ 'CY': 'Encomenda Internacional - Colis',
+ 'CZ': 'Encomenda Internacional - Colis',
+ 'DA': 'SEDEX ou Remessa Expressa com AR Digital',
+ 'DB': 'SEDEX ou Remessa Expressa com AR Digital (Bradesco)',
+ 'DC': 'Remessa Expressa CRLV/CRV/CNH e Notificações',
+ 'DD': 'Devolução de documentos',
+ 'DE': 'Remessa Expressa Talão/Cartão com AR',
+ 'DF': 'e-SEDEX',
+ 'DG': 'SEDEX',
+ 'DI': 'SEDEX ou Remessa Expressa com AR Digital (Itaú)',
+ 'DJ': 'SEDEX',
+ 'DK': 'PAC Extra Grande',
+ 'DL': 'SEDEX',
+ 'DM': 'e-SEDEX',
+ 'DN': 'SEDEX',
+ 'DO': 'SEDEX ou Remessa Expressa com AR Digital (Itaú)',
+ 'DP': 'SEDEX Pagamento na Entrega',
+ 'DQ': 'SEDEX ou Remessa Expressa com AR Digital (Santander)',
+ 'DR': 'Remessa Expressa com AR Digital (Santander)',
+ 'DS': 'SEDEX ou Remessa Expressa com AR Digital (Santander)',
+ 'DT': 'Remessa econômica com AR Digital (DETRAN)',
+ 'DU': 'e-SEDEX',
+ 'DV': 'SEDEX c/ AR digital',
+ 'DW': 'Encomenda SEDEX (Etiqueta Lógica)',
+ 'DX': 'SEDEX 10',
+ 'EA': 'Encomenda Internacional - EMS',
+ 'EB': 'Encomenda Internacional - EMS',
+ 'EC': 'PAC',
+ 'ED': 'Packet Express',
+ 'EE': 'Encomenda Internacional - EMS',
+ 'EF': 'Encomenda Internacional - EMS',
+ 'EG': 'Encomenda Internacional - EMS',
+ 'EH': 'Encomenda Internacional - EMS ou Encomenda com AR Digital',
+ 'EI': 'Encomenda Internacional - EMS',
+ 'EJ': 'Encomenda Internacional - EMS',
+ 'EK': 'Encomenda Internacional - EMS',
+ 'EL': 'Encomenda Internacional - EMS',
+ 'EN': 'Encomenda Internacional - EMS',
+ 'EO': 'Encomenda Internacional - EMS',
+ 'EP': 'Encomenda Internacional - EMS',
+ 'EQ': 'Encomenda de serviço não expressa (ECT)',
+ 'ER': 'Objeto registrado',
+ 'ES': 'e-SEDEX ou EMS',
+ 'ET': 'Encomenda Internacional - EMS',
+ 'EU': 'Encomenda Internacional - EMS',
+ 'EV': 'Encomenda Internacional - EMS',
+ 'EW': 'Encomenda Internacional - EMS',
+ 'EX': 'Encomenda Internacional - EMS',
+ 'EY': 'Encomenda Internacional - EMS',
+ 'EZ': 'Encomenda Internacional - EMS',
+ 'FA': 'FAC registrado',
+ 'FE': 'Encomenda FNDE',
+ 'FF': 'Objeto registrado (DETRAN)',
+ 'FH': 'FAC registrado com AR Digital',
+ 'FM': 'FAC monitorado',
+ 'FR': 'FAC registrado',
+ 'IA': 'Logística Integrada (agendado / avulso)',
+ 'IC': 'Logística Integrada (a cobrar)',
+ 'ID': 'Logística Integrada (devolução de documento)',
+ 'IE': 'Logística Integrada (Especial)',
+ 'IF': 'CPF',
+ 'II': 'Logística Integrada (ECT)',
+ 'IK': 'Logística Integrada com Coleta Simultânea',
+ 'IM': 'Logística Integrada (Medicamentos)',
+ 'IN': 'Correspondência e EMS recebido do Exterior',
+ 'IP': 'Logística Integrada (Programada)',
+ 'IR': 'Impresso Registrado',
+ 'IS': 'Logística integrada standard (medicamentos)',
+ 'IT': 'Remessa Expressa Medicamentos / Logística Integrada Termolábil',
+ 'IU': 'Logística Integrada (urgente)',
+ 'IX': 'EDEI Expresso',
+ 'JA': 'Remessa econômica com AR Digital',
+ 'JB': 'Remessa econômica com AR Digital',
+ 'JC': 'Remessa econômica com AR Digital',
+ 'JD': 'Remessa econômica Talão/Cartão',
+ 'JE': 'Remessa econômica com AR Digital',
+ 'JF': 'Remessa econômica com AR Digital',
+ 'JG': 'Objeto registrado urgente/prioritário',
+ 'JH': 'Objeto registrado urgente / prioritário',
+ 'JI': 'Remessa econômica Talão/Cartão',
+ 'JJ': 'Objeto registrado (Justiça)',
+ 'JK': 'Remessa econômica Talão/Cartão',
+ 'JL': 'Objeto registrado',
+ 'JM': 'Mala Direta Postal Especial',
+ 'JN': 'Objeto registrado econômico',
+ 'JO': 'Objeto registrado urgente',
+ 'JP': 'Receita Federal',
+ 'JQ': 'Remessa econômica com AR Digital',
+ 'JR': 'Objeto registrado urgente / prioritário',
+ 'JS': 'Objeto registrado',
+ 'JT': 'Objeto Registrado Urgente',
+ 'JV': 'Remessa Econômica (c/ AR DIGITAL)',
+ 'LA': 'SEDEX com Logística Reversa Simultânea em Agência',
+ 'LB': 'e-SEDEX com Logística Reversa Simultânea em Agência',
+ 'LC': 'Objeto Internacional (Prime)',
+ 'LE': 'Logística Reversa Econômica',
+ 'LF': 'Objeto Internacional (Prime)',
+ 'LI': 'Objeto Internacional (Prime)',
+ 'LJ': 'Objeto Internacional (Prime)',
+ 'LK': 'Objeto Internacional (Prime)',
+ 'LM': 'Objeto Internacional (Prime)',
+ 'LN': 'Objeto Internacional (Prime)',
+ 'LP': 'PAC com Logística Reversa Simultânea em Agência',
+ 'LS': 'SEDEX Logística Reversa',
+ 'LV': 'Logística Reversa Expressa',
+ 'LX': 'Packet Standard / Econômica',
+ 'LZ': 'Objeto Internacional (Prime)',
+ 'MA': 'Serviços adicionais do Telegrama',
+ 'MB': 'Telegrama (balcão)',
+ 'MC': 'Telegrama (Fonado)',
+ 'MD': 'SEDEX Mundi (Documento interno)',
+ 'ME': 'Telegrama',
+ 'MF': 'Telegrama (Fonado)',
+ 'MK': 'Telegrama (corporativo)',
+ 'ML': 'Fecha Malas (Rabicho)',
+ 'MM': 'Telegrama (Grandes clientes)',
+ 'MP': 'Telegrama (Pré-pago)',
+ 'MR': 'AR digital',
+ 'MS': 'Encomenda Saúde',
+ 'MT': 'Telegrama (Telemail)',
+ 'MY': 'Telegrama internacional (entrante)',
+ 'MZ': 'Telegrama (Correios Online)',
+ 'NE': 'Tele Sena resgatada',
+ 'NX': 'EDEI Econômico (não urgente)',
+ 'PA': 'Passaporte',
+ 'PB': 'PAC',
+ 'PC': 'PAC a Cobrar',
+ 'PD': 'PAC',
+ 'PE': 'PAC',
+ 'PF': 'Passaporte',
+ 'PG': 'PAC',
+ 'PH': 'PAC',
+ 'PI': 'PAC',
+ 'PJ': 'PAC',
+ 'PK': 'PAC Extra Grande',
+ 'PL': 'PAC',
+ 'PN': 'PAC',
+ 'PR': 'Reembolso Postal',
+ 'QQ': 'Objeto de teste (SIGEP Web)',
+ 'RA': 'Objeto registrado / prioritário',
+ 'RB': 'Carta registrada',
+ 'RC': 'Carta registrada com Valor Declarado',
+ 'RD': 'Remessa econômica ou objeto registrado (DETRAN)',
+ 'RE': 'Objeto registrado econômico',
+ 'RF': 'Receita Federal',
+ 'RG': 'Objeto registrado',
+ 'RH': 'Objeto registrado com AR Digital',
+ 'RI': 'Objeto registrado internacional prioritário',
+ 'RJ': 'Objeto registrado',
+ 'RK': 'Objeto registrado',
+ 'RL': 'Objeto registrado',
+ 'RM': 'Objeto registrado urgente',
+ 'RN': 'Objeto registrado (SIGEPWEB ou Agência)',
+ 'RO': 'Objeto registrado',
+ 'RP': 'Reembolso Postal',
+ 'RQ': 'Objeto registrado',
+ 'RR': 'Objeto registrado',
+ 'RS': 'Objeto registrado',
+ 'RT': 'Remessa econômica Talão/Cartão',
+ 'RU': 'Objeto registrado (ECT)',
+ 'RV': 'Remessa econômica CRLV/CRV/CNH e Notificações com AR Digital',
+ 'RW': 'Objeto internacional',
+ 'RX': 'Objeto internacional',
+ 'RY': 'Remessa econômica Talão/Cartão com AR Digital',
+ 'RZ': 'Objeto registrado',
+ 'SA': 'SEDEX',
+ 'SB': 'SEDEX 10',
+ 'SC': 'SEDEX a cobrar',
+ 'SD': 'SEDEX ou Remessa Expressa (DETRAN)',
+ 'SE': 'SEDEX',
+ 'SF': 'SEDEX',
+ 'SG': 'SEDEX',
+ 'SH': 'SEDEX com AR Digital / SEDEX ou AR Digital',
+ 'SI': 'SEDEX',
+ 'SJ': 'SEDEX Hoje',
+ 'SK': 'SEDEX',
+ 'SL': 'SEDEX',
+ 'SM': 'SEDEX 12',
+ 'SN': 'SEDEX',
+ 'SO': 'SEDEX',
+ 'SP': 'SEDEX Pré-franqueado',
+ 'SQ': 'SEDEX',
+ 'SR': 'SEDEX',
+ 'SS': 'SEDEX',
+ 'ST': 'Remessa Expressa Talão/Cartão',
+ 'SU': 'Encomenda de serviço expressa (ECT)',
+ 'SV': 'Remessa Expressa CRLV/CRV/CNH e Notificações com AR Digital',
+ 'SW': 'e-SEDEX',
+ 'SX': 'SEDEX 10',
+ 'SY': 'Remessa Expressa Talão/Cartão com AR Digital',
+ 'SZ': 'SEDEX',
+ 'TC': 'Objeto para treinamento',
+ 'TE': 'Objeto para treinamento',
+ 'TS': 'Objeto para treinamento',
+ 'VA': 'Encomendas com valor declarado',
+ 'VC': 'Encomendas',
+ 'VD': 'Encomendas com valor declarado',
+ 'VE': 'Encomendas',
+ 'VF': 'Encomendas com valor declarado',
+ 'VV': 'Objeto internacional',
+ 'XA': 'Aviso de chegada (internacional)',
+ 'XM': 'SEDEX Mundi',
+ 'XR': 'Encomenda SUR Postal Expresso',
+ 'XX': 'Encomenda SUR Postal 24 horas'
+}
+
+function matchesPattern (object) {
+ return /^([A-Z]{2}[0-9]{9}[A-Z]{2}){1}$/.test(object)
+}
+
+function category(object) {
+ if ( !matchesPattern(object) || !Helpers.isString(object) ) {
+ return undefined
+ }
+ let initials = object.substr(0, 2)
+ let objectCategory
+ switch (initials) {
+
+ case 'EM':
+ let final = object.substr(object.length - 2, 2)
+ objectCategory = "Encomenda Internacional - EMS Importação"
+ if (final === "BR") {
+ objectCategory = "Encomenda Internacional - SEDEX Mundi"
+ }
+ break
+
+ default:
+ objectCategory = validInitials[initials]
+
+ }
+ return objectCategory
+}
+
+function isValid (object) {
+ return category(object) !== undefined
+}
+
+module.exports = {
+ category, isValid
+}
diff --git a/tests/e2e/track.spec.js b/tests/e2e/track.spec.js
new file mode 100644
index 0000000..b793abc
--- /dev/null
+++ b/tests/e2e/track.spec.js
@@ -0,0 +1,86 @@
+'use strict'
+
+const chai = require('chai')
+const chaiAsPromised = require('chai-as-promised')
+const chaiSubset = require('chai-subset')
+
+const Tracking = require('../../src/track')
+const TrackingError = require('../../src/errors/tracking')
+
+chai.use(chaiAsPromised)
+chai.use(chaiSubset)
+
+const expect = chai.expect
+
+describe('tracking-correios (E2E)', () => {
+
+ describe('when invoked with valid tracking number', () => {
+ describe('with one', () => {
+ let testFile = require('../responses/valid-one.json')
+ it('should fulfill with correct tracking information (string)', (done) => {
+ expect(Tracking.track('DU897123996BR')).to.eventually.deep.equal(testFile).notify(done)
+ })
+ it('should fulfill with correct tracking information (array)', (done) => {
+ expect(Tracking.track(['DU897123996BR'])).to.eventually.deep.equal(testFile).notify(done)
+ })
+ })
+
+ describe('with "n"', () => {
+ let testFile = require('../responses/valid-two.json')
+ it('should fulfill with correct tracking information', (done) => {
+ expect(Tracking.track(['DU897123996BR', 'PN273603577BR'])).to.eventually.deep.equal(testFile).notify(done)
+ })
+ })
+ })
+
+ describe('when invoked with invalid tracking number', () => {
+ it('should reject with a validation_error type with empty call', () => {
+ return Tracking.track()
+ .catch((error) => {
+ return expect(error)
+ .to.be.an.instanceOf(TrackingError)
+ .and.containSubset({
+ name: 'TrackingError',
+ message: 'Erro ao validar os objetos.',
+ type: 'validation_error',
+ errors: [{
+ message: 'Nenhum objeto válido para pesquisa.',
+ service: 'objects_validation'
+ }]
+ })
+ })
+ })
+ it('should reject with a validation_error type (one)', () => {
+ return Tracking.track(['AB123123ABCBR'])
+ .catch((error) => {
+ return expect(error)
+ .to.be.an.instanceOf(TrackingError)
+ .and.containSubset({
+ name: 'TrackingError',
+ message: 'Erro ao validar os objetos.',
+ type: 'validation_error',
+ errors: [{
+ message: 'Nenhum objeto válido para pesquisa.',
+ service: 'objects_validation'
+ }]
+ })
+ })
+ })
+ it('should reject with a validation_error type (several)', () => {
+ return Tracking.track(['AB123123ABCBR', 'ABCD', '123123123', 'BR', '%$@!$!', 'AB1231234564BR', 'ABR123123123BR', 'AB231234564ABB'])
+ .catch((error) => {
+ return expect(error)
+ .to.be.an.instanceOf(TrackingError)
+ .and.containSubset({
+ name: 'TrackingError',
+ message: 'Erro ao validar os objetos.',
+ type: 'validation_error',
+ errors: [{
+ message: 'Nenhum objeto válido para pesquisa.',
+ service: 'objects_validation'
+ }]
+ })
+ })
+ })
+ })
+})
diff --git a/tests/responses/valid-one.json b/tests/responses/valid-one.json
new file mode 100644
index 0000000..d7f3556
--- /dev/null
+++ b/tests/responses/valid-one.json
@@ -0,0 +1,99 @@
+{
+ "numero":"DU897123996BR",
+ "sigla":"DU",
+ "nome":"ENCOMENDA E-SEDEX",
+ "categoria":"E-SEDEX",
+ "evento":[
+ {
+ "tipo":"BDE",
+ "status":"01",
+ "data":"12/12/2016",
+ "hora":"19:06",
+ "descricao":"Objeto entregue ao destinatário",
+ "recebedor":"",
+ "documento":"",
+ "comentario":"",
+ "local":"CEE BAURU",
+ "codigo":"17034972",
+ "cidade":"Bauru",
+ "uf":"SP"
+ },
+ {
+ "tipo":"OEC",
+ "status":"01",
+ "data":"12/12/2016",
+ "hora":"10:30",
+ "descricao":"Objeto saiu para entrega ao destinatário",
+ "local":"CEE BAURU",
+ "codigo":"17034972",
+ "cidade":"Bauru",
+ "uf":"SP"
+ },
+ {
+ "tipo":"RO",
+ "status":"01",
+ "data":"12/12/2016",
+ "hora":"04:34",
+ "descricao":"Objeto encaminhado",
+ "local":"CTCE BAURU",
+ "codigo":"17034970",
+ "cidade":"Bauru",
+ "uf":"SP",
+ "destino":{
+ "local":"CEE BAURU",
+ "codigo":"17034972",
+ "cidade":"Bauru",
+ "bairro":"Distrito Industrial",
+ "uf":"SP"
+ }
+ },
+ {
+ "tipo":"RO",
+ "status":"01",
+ "data":"08/12/2016",
+ "hora":"21:13",
+ "descricao":"Objeto encaminhado",
+ "local":"CTCE LONDRINA",
+ "codigo":"86066970",
+ "cidade":"LONDRINA",
+ "uf":"PR",
+ "destino":{
+ "local":"CTCE BAURU",
+ "codigo":"17034970",
+ "cidade":"Bauru",
+ "bairro":"Distrito Industrial",
+ "uf":"SP"
+ }
+ },
+ {
+ "tipo":"RO",
+ "status":"01",
+ "data":"08/12/2016",
+ "hora":"13:37",
+ "descricao":"Objeto encaminhado",
+ "local":"AGF JARDIM ALVORADA",
+ "codigo":"87030981",
+ "cidade":"Maringa",
+ "uf":"PR",
+ "destino":{
+ "local":"CTCE LONDRINA",
+ "codigo":"86066970",
+ "cidade":"LONDRINA",
+ "bairro":"JARDIM COLUMBIA",
+ "uf":"PR"
+ }
+ },
+ {
+ "tipo":"PO",
+ "status":"09",
+ "data":"07/12/2016",
+ "hora":"17:37",
+ "descricao":"Objeto postado após o horário limite da agência",
+ "detalhe":"Objeto sujeito a encaminhamento no próximo dia útil",
+ "local":"AGF JARDIM ALVORADA",
+ "codigo":"87030981",
+ "cidade":"Maringa",
+ "uf":"PR"
+ }
+ ]
+}
diff --git a/tests/responses/valid-two.json b/tests/responses/valid-two.json
new file mode 100644
index 0000000..e82a230
--- /dev/null
+++ b/tests/responses/valid-two.json
@@ -0,0 +1,199 @@
+[
+ {
+ "numero":"DU897123996BR",
+ "sigla":"DU",
+ "nome":"ENCOMENDA E-SEDEX",
+ "categoria":"E-SEDEX",
+ "evento":[
+ {
+ "tipo":"BDE",
+ "status":"01",
+ "data":"12/12/2016",
+ "hora":"19:06",
+ "descricao":"Objeto entregue ao destinatário",
+ "recebedor":"",
+ "documento":"",
+ "comentario":"",
+ "local":"CEE BAURU",
+ "codigo":"17034972",
+ "cidade":"Bauru",
+ "uf":"SP"
+ },
+ {
+ "tipo":"OEC",
+ "status":"01",
+ "data":"12/12/2016",
+ "hora":"10:30",
+ "descricao":"Objeto saiu para entrega ao destinatário",
+ "local":"CEE BAURU",
+ "codigo":"17034972",
+ "cidade":"Bauru",
+ "uf":"SP"
+ },
+ {
+ "tipo":"RO",
+ "status":"01",
+ "data":"12/12/2016",
+ "hora":"04:34",
+ "descricao":"Objeto encaminhado",
+ "local":"CTCE BAURU",
+ "codigo":"17034970",
+ "cidade":"Bauru",
+ "uf":"SP",
+ "destino":{
+ "local":"CEE BAURU",
+ "codigo":"17034972",
+ "cidade":"Bauru",
+ "bairro":"Distrito Industrial",
+ "uf":"SP"
+ }
+ },
+ {
+ "tipo":"RO",
+ "status":"01",
+ "data":"08/12/2016",
+ "hora":"21:13",
+ "descricao":"Objeto encaminhado",
+ "local":"CTCE LONDRINA",
+ "codigo":"86066970",
+ "cidade":"LONDRINA",
+ "uf":"PR",
+ "destino":{
+ "local":"CTCE BAURU",
+ "codigo":"17034970",
+ "cidade":"Bauru",
+ "bairro":"Distrito Industrial",
+ "uf":"SP"
+ }
+ },
+ {
+ "tipo":"RO",
+ "status":"01",
+ "data":"08/12/2016",
+ "hora":"13:37",
+ "descricao":"Objeto encaminhado",
+ "local":"AGF JARDIM ALVORADA",
+ "codigo":"87030981",
+ "cidade":"Maringa",
+ "uf":"PR",
+ "destino":{
+ "local":"CTCE LONDRINA",
+ "codigo":"86066970",
+ "cidade":"LONDRINA",
+ "bairro":"JARDIM COLUMBIA",
+ "uf":"PR"
+ }
+ },
+ {
+ "tipo":"PO",
+ "status":"09",
+ "data":"07/12/2016",
+ "hora":"17:37",
+ "descricao":"Objeto postado após o horário limite da agência",
+ "detalhe":"Objeto sujeito a encaminhamento no próximo dia útil",
+ "local":"AGF JARDIM ALVORADA",
+ "codigo":"87030981",
+ "cidade":"Maringa",
+ "uf":"PR"
+ }
+ ]
+ },
+ {
+ "numero":"PN273603577BR",
+ "sigla":"PN",
+ "nome":"ENCOMENDA PAC (ETIQ LOGICA)",
+ "categoria":"ENCOMENDA PAC",
+ "evento":[
+ {
+ "tipo":"BDE",
+ "status":"01",
+ "data":"28/10/2016",
+ "hora":"18:24",
+ "descricao":"Objeto entregue ao destinatário",
+ "recebedor":"",
+ "documento":"",
+ "comentario":"",
+ "local":"CEE BAURU",
+ "codigo":"17034972",
+ "cidade":"Bauru",
+ "uf":"SP"
+ },
+ {
+ "tipo":"OEC",
+ "status":"01",
+ "data":"28/10/2016",
+ "hora":"11:29",
+ "descricao":"Objeto saiu para entrega ao destinatário",
+ "local":"CEE BAURU",
+ "codigo":"17034972",
+ "cidade":"Bauru",
+ "uf":"SP"
+ },
+ {
+ "tipo":"RO",
+ "status":"01",
+ "data":"27/10/2016",
+ "hora":"13:02",
+ "descricao":"Objeto encaminhado",
+ "local":"CTCE BAURU",
+ "codigo":"17034970",
+ "cidade":"Bauru",
+ "uf":"SP",
+ "destino":{
+ "local":"CEE BAURU",
+ "codigo":"17034972",
+ "cidade":"Bauru",
+ "bairro":"Distrito Industrial",
+ "uf":"SP"
+ }
+ },
+ {
+ "tipo":"DO",
+ "status":"01",
+ "data":"26/10/2016",
+ "hora":"19:08",
+ "descricao":"Objeto encaminhado",
+ "local":"AGF OZANAN",
+ "codigo":"13215971",
+ "cidade":"Jundiai",
+ "uf":"SP",
+ "destino":{
+ "local":"CTE CAMPINAS",
+ "codigo":"13050971",
+ "cidade":"INDAIATUBA",
+ "bairro":"HELVETIA",
+ "uf":"SP"
+ }
+ },
+ {
+ "tipo":"RO",
+ "status":"01",
+ "data":"26/10/2016",
+ "hora":"14:34",
+ "descricao":"Objeto encaminhado",
+ "local":"CTE CAMPINAS",
+ "codigo":"13050971",
+ "cidade":"INDAIATUBA",
+ "uf":"SP",
+ "destino":{
+ "local":"CTCE BAURU",
+ "codigo":"17034970",
+ "cidade":"Bauru",
+ "bairro":"Distrito Industrial",
+ "uf":"SP"
+ }
+ },
+ {
+ "tipo":"PO",
+ "status":"01",
+ "data":"25/10/2016",
+ "hora":"16:35",
+ "descricao":"Objeto postado",
+ "local":"AGF OZANAN",
+ "codigo":"13215971",
+ "cidade":"Jundiai",
+ "uf":"SP"
+ }
+ ]
+ }
+]
diff --git a/tests/unit/fixtures/response-valid-one.xml b/tests/unit/fixtures/response-valid-one.xml
new file mode 100644
index 0000000..2deffa1
--- /dev/null
+++ b/tests/unit/fixtures/response-valid-one.xml
@@ -0,0 +1,110 @@
+
+
+ pid=36263,requestid=2cb01ac165f9c688f949b4a985e0531286a43f3af2e7fbe9
+
+
+
+
+ 2.0
+ 1
+
+ DU897123996BR
+ DU
+ ENCOMENDA E-SEDEX
+ E-SEDEX
+
+ BDE
+ 01
+ 12/12/2016
+ 19:06
+ Objeto entregue ao destinatário
+
+
+
+ CEE BAURU
+ 17034972
+ Bauru
+ SP
+
+
+ OEC
+ 01
+ 12/12/2016
+ 10:30
+ Objeto saiu para entrega ao destinatário
+ CEE BAURU
+ 17034972
+ Bauru
+ SP
+
+
+ RO
+ 01
+ 12/12/2016
+ 04:34
+ Objeto encaminhado
+ CTCE BAURU
+ 17034970
+ Bauru
+ SP
+
+ CEE BAURU
+ 17034972
+ Bauru
+ Distrito Industrial
+ SP
+
+
+
+ RO
+ 01
+ 08/12/2016
+ 21:13
+ Objeto encaminhado
+ CTCE LONDRINA
+ 86066970
+ LONDRINA
+ PR
+
+ CTCE BAURU
+ 17034970
+ Bauru
+ Distrito Industrial
+ SP
+
+
+
+ RO
+ 01
+ 08/12/2016
+ 13:37
+ Objeto encaminhado
+ AGF JARDIM ALVORADA
+ 87030981
+ Maringa
+ PR
+
+ CTCE LONDRINA
+ 86066970
+ LONDRINA
+ JARDIM COLUMBIA
+ PR
+
+
+
+ PO
+ 09
+ 07/12/2016
+ 17:37
+ Objeto postado após o horário limite da agência
+ Objeto sujeito a encaminhamento no próximo dia útil
+ AGF JARDIM ALVORADA
+ 87030981
+ Maringa
+ PR
+
+
+
+
+
+
diff --git a/tests/unit/fixtures/response-valid-two.xml b/tests/unit/fixtures/response-valid-two.xml
new file mode 100644
index 0000000..8f18a28
--- /dev/null
+++ b/tests/unit/fixtures/response-valid-two.xml
@@ -0,0 +1,206 @@
+
+
+ pid=130635,requestid=abd12a3d40122ab62b34d9e70d5c29eecbe835bf7b91d67e
+
+
+
+
+ 2.0
+ 2
+
+ DU897123996BR
+ DU
+ ENCOMENDA E-SEDEX
+ E-SEDEX
+
+ BDE
+ 01
+ 12/12/2016
+ 19:06
+ Objeto entregue ao destinatário
+
+
+
+ CEE BAURU
+ 17034972
+ Bauru
+ SP
+
+
+ OEC
+ 01
+ 12/12/2016
+ 10:30
+ Objeto saiu para entrega ao destinatário
+ CEE BAURU
+ 17034972
+ Bauru
+ SP
+
+
+ RO
+ 01
+ 12/12/2016
+ 04:34
+ Objeto encaminhado
+ CTCE BAURU
+ 17034970
+ Bauru
+ SP
+
+ CEE BAURU
+ 17034972
+ Bauru
+ Distrito Industrial
+ SP
+
+
+
+ RO
+ 01
+ 08/12/2016
+ 21:13
+ Objeto encaminhado
+ CTCE LONDRINA
+ 86066970
+ LONDRINA
+ PR
+
+ CTCE BAURU
+ 17034970
+ Bauru
+ Distrito Industrial
+ SP
+
+
+
+ RO
+ 01
+ 08/12/2016
+ 13:37
+ Objeto encaminhado
+ AGF JARDIM ALVORADA
+ 87030981
+ Maringa
+ PR
+
+ CTCE LONDRINA
+ 86066970
+ LONDRINA
+ JARDIM COLUMBIA
+ PR
+
+
+
+ PO
+ 09
+ 07/12/2016
+ 17:37
+ Objeto postado após o horário limite da agência
+ Objeto sujeito a encaminhamento no próximo dia útil
+ AGF JARDIM ALVORADA
+ 87030981
+ Maringa
+ PR
+
+
+
+ PN273603577BR
+ PN
+ ENCOMENDA PAC (ETIQ LOGICA)
+ ENCOMENDA PAC
+
+ BDE
+ 01
+ 28/10/2016
+ 18:24
+ Objeto entregue ao destinatário
+
+
+
+ CEE BAURU
+ 17034972
+ Bauru
+ SP
+
+
+ OEC
+ 01
+ 28/10/2016
+ 11:29
+ Objeto saiu para entrega ao destinatário
+ CEE BAURU
+ 17034972
+ Bauru
+ SP
+
+
+ RO
+ 01
+ 27/10/2016
+ 13:02
+ Objeto encaminhado
+ CTCE BAURU
+ 17034970
+ Bauru
+ SP
+
+ CEE BAURU
+ 17034972
+ Bauru
+ Distrito Industrial
+ SP
+
+
+
+ DO
+ 01
+ 26/10/2016
+ 19:08
+ Objeto encaminhado
+ AGF OZANAN
+ 13215971
+ Jundiai
+ SP
+
+ CTE CAMPINAS
+ 13050971
+ INDAIATUBA
+ HELVETIA
+ SP
+
+
+
+ RO
+ 01
+ 26/10/2016
+ 14:34
+ Objeto encaminhado
+ CTE CAMPINAS
+ 13050971
+ INDAIATUBA
+ SP
+
+ CTCE BAURU
+ 17034970
+ Bauru
+ Distrito Industrial
+ SP
+
+
+
+ PO
+ 01
+ 25/10/2016
+ 16:35
+ Objeto postado
+ AGF OZANAN
+ 13215971
+ Jundiai
+ SP
+
+
+
+
+
+
diff --git a/tests/unit/track.spec.js b/tests/unit/track.spec.js
new file mode 100644
index 0000000..9d7990f
--- /dev/null
+++ b/tests/unit/track.spec.js
@@ -0,0 +1,249 @@
+'use strict'
+
+const chai = require('chai')
+const chaiAsPromised = require('chai-as-promised')
+const chaiSubset = require('chai-subset')
+
+const nock = require('nock')
+const path = require('path')
+
+const Tracking = require('../../src/track')
+const TrackingError = require('../../src/errors/tracking')
+
+chai.use(chaiAsPromised)
+chai.use(chaiSubset)
+
+const expect = chai.expect
+
+describe('tracking-correios (unit)', () => {
+
+ describe('when imported', () => {
+ it('should return an object', () => {
+ expect(Tracking).to.be.a('object')
+ })
+ it('should return a "track" method', () => {
+ expect(Tracking.track).to.be.a('function')
+ })
+ it('should return a "validate" method', () => {
+ expect(Tracking.validate).to.be.a('function')
+ })
+ it('should return a "isValid" method', () => {
+ expect(Tracking.isValid).to.be.a('function')
+ })
+ it('should return a "filter" method', () => {
+ expect(Tracking.filter).to.be.a('function')
+ })
+ })
+
+ describe('when "track" invoked', () => {
+ it('should return a Promise', () => {
+ nock('http://webservice.correios.com.br')
+ .post('/service/rastro')
+ .replyWithFile(200, path.join(__dirname, '/fixtures/response-valid-one.xml'))
+
+ const trackingCorreios = Tracking.track('DU897123996BR')
+ expect(trackingCorreios.then).to.be.a('function')
+ expect(trackingCorreios.catch).to.be.a('function')
+ })
+
+ describe('with one valid tracking number', () => {
+ let testFile = require('../responses/valid-one.json')
+ it('should fulfill with correct tracking information using simple string', () => {
+ nock('http://webservice.correios.com.br')
+ .post('/service/rastro')
+ .replyWithFile(200, path.join(__dirname, '/fixtures/response-valid-one.xml'))
+
+ return expect(Tracking.track('DU897123996BR')).to.eventually.deep.equal(testFile)
+ })
+ it('should fulfill with correct tracking information using array', () => {
+ nock('http://webservice.correios.com.br')
+ .post('/service/rastro')
+ .replyWithFile(200, path.join(__dirname, '/fixtures/response-valid-one.xml'))
+
+ return expect(Tracking.track(['DU897123996BR'])).to.eventually.deep.equal(testFile)
+ })
+ })
+
+ describe('with two valid tracking numbers', () => {
+ let testFile = require('../responses/valid-two.json')
+ it('should fulfill with correct tracking information using array', () => {
+ nock('http://webservice.correios.com.br')
+ .post('/service/rastro')
+ .replyWithFile(200, path.join(__dirname, '/fixtures/response-valid-two.xml'))
+
+ return expect(Tracking.track(['DU897123996BR', 'PN273603577BR'])).to.eventually.deep.equal(testFile)
+ })
+ })
+ })
+
+ describe('when "validate" invoked', () => {
+ it('should return an object', () => {
+ expect(Tracking.validate('DU897123996BR')).to.be.a('object')
+ })
+
+ describe('with no parameters', () => {
+ it('should return valid and invalid unfilled', () => {
+ return expect(Tracking.validate())
+ .to.deep.equal({
+ "valid": [],
+ "invalid": []
+ })
+ })
+ })
+
+ describe('with empty string as parameter', () => {
+ it('should return valid and invalid unfilled', () => {
+ return expect(Tracking.validate(""))
+ .to.deep.equal({
+ "valid": [],
+ "invalid": [""]
+ })
+ })
+ })
+
+ describe('with one valid tracking numbers', () => {
+ it('should return valid object filled and invalid unfilled (string)', () => {
+ return expect(Tracking.validate('DU897123996BR'))
+ .to.deep.equal({
+ "valid": ['DU897123996BR'],
+ "invalid": []
+ })
+ })
+ it('should return valid object filled and invalid unfilled (array)', () => {
+ return expect(Tracking.validate(['DU897123996BR']))
+ .to.deep.equal({
+ "valid": ['DU897123996BR'],
+ "invalid": []
+ })
+ })
+ })
+
+ describe('with "n" valid tracking numbers', () => {
+ it('should return valid object filled and invalid unfilled', () => {
+ return expect(Tracking.validate(['DU897123996BR', 'PN273603577BR', 'PN306956971BR']))
+ .to.deep.equal({
+ "valid": ['DU897123996BR', 'PN273603577BR', 'PN306956971BR'],
+ "invalid": []
+ })
+ })
+ })
+
+ describe('with 1 valid and 1 invalid tracking numbers', () => {
+ it('should return valid object filled with valid and invalid filled with invalid', () => {
+ return expect(Tracking.validate(['DU897123996BR', 'ABC' ]))
+ .to.deep.equal({
+ "valid": ['DU897123996BR'],
+ "invalid": ['ABC']
+ })
+ })
+ })
+
+ describe('with "n" valid and 1 invalid tracking numbers', () => {
+ it('should return valid object filled with valid and invalid filled with invalid', () => {
+ return expect(Tracking.validate(['DU897123996BR', 'PN273603577BR', 'PN306956971BR', 'ABC' ]))
+ .to.deep.equal({
+ "valid": ['DU897123996BR', 'PN273603577BR', 'PN306956971BR'],
+ "invalid": ['ABC']
+ })
+ })
+ })
+
+ describe('with 1 valid and "n" invalid tracking numbers', () => {
+ it('should return valid object filled with valid and invalid filled with invalid', () => {
+ return expect(Tracking.validate(['DU897123996BR', 'AABBBCCCC', 'ASDQ@!#!%!123', 'ABC' ]))
+ .to.deep.equal({
+ "valid": ['DU897123996BR'],
+ "invalid": ['AABBBCCCC', 'ASDQ@!#!%!123', 'ABC']
+ })
+ })
+ })
+
+ describe('with "n" valid and "n" invalid tracking numbers', () => {
+ it('should return valid object filled with valid and invalid filled with invalid', () => {
+ return expect(Tracking.validate(['DU897123996BR', 'PN273603577BR', 'PN306956971BR', 'AABBBCCCC', 'ASDQ@!#!%!123', 'ABC' ]))
+ .to.deep.equal({
+ "valid": ['DU897123996BR', 'PN273603577BR', 'PN306956971BR'],
+ "invalid": ['AABBBCCCC', 'ASDQ@!#!%!123', 'ABC']
+ })
+ })
+ })
+ })
+
+ describe('when "isValid" invoked', () => {
+ it('should return a boolean', () => {
+ expect(Tracking.isValid('DU897123996BR')).to.be.a('boolean')
+ })
+
+ describe('with empty value', () => {
+ it('should return false', () => {
+ expect(Tracking.isValid())
+ .to.deep.equal(false)
+ })
+ })
+
+ describe('with valid object', () => {
+ it('should return true', () => {
+ expect(Tracking.isValid('DU897123996BR'))
+ .to.deep.equal(true)
+ })
+ })
+
+ describe('with invalid object (wrong initials)', () => {
+ it('should return false', () => {
+ expect(Tracking.isValid('AA123123123BR'))
+ .to.deep.equal(false)
+ })
+ })
+
+ describe('with invalid object (wrong format)', () => {
+ it('should return false (simple)', () => {
+ expect(Tracking.isValid('AAAAA'))
+ .to.deep.equal(false)
+ })
+ it('should return false (complex)', () => {
+ expect(Tracking.isValid('AAA123123123BR'))
+ .to.deep.equal(false)
+ })
+ })
+
+ describe('with invalid object (not string)', () => {
+ it('should return false (array)', () => {
+ expect(Tracking.isValid([]))
+ .to.deep.equal(false)
+ })
+ it('should return false (object)', () => {
+ expect(Tracking.isValid({}))
+ .to.deep.equal(false)
+ })
+ })
+ })
+
+ describe('when "filter" invoked', () => {
+ it('should return an array', () => {
+ expect(Tracking.filter('DU897123996BR')).to.be.a('array')
+ })
+
+ describe('with valid objects', () => {
+ it('should return array filled (one)', () => {
+ expect(Tracking.filter('DU897123996BR'))
+ .to.deep.equal(['DU897123996BR'])
+ })
+ it('should return array filled ("n")', () => {
+ expect(Tracking.filter(['DU897123996BR', 'PN273603577BR', 'PN306956971BR']))
+ .to.deep.equal(['DU897123996BR', 'PN273603577BR', 'PN306956971BR'])
+ })
+ })
+
+ describe('with invalid objects', () => {
+ it('should return array unfilled (one)', () => {
+ expect(Tracking.filter('AAAAA'))
+ .to.deep.equal([])
+ })
+ it('should return array unfilled ("n")', () => {
+ expect(Tracking.filter(['AB123123ABCBR', 'ABCD', '123123123', 'BR', '%$@!$!', 'AB1231234564BR', 'ABR123123123BR', 'AB231234564ABB']))
+ .to.deep.equal([])
+ })
+ })
+ })
+
+})