Skip to content

Commit

Permalink
feat(barcode): differentiate between 404/400
Browse files Browse the repository at this point in the history
This commit differentiates between a "NOT FOUND" barcode (doesn't exist
in the database) and all other errors.  In theory, we shouldn't
encounter a "NOT FOUND" error if users are only scanning barcodes that
are generated by the system, unless someone has deleted it.

This also refactors the tests to remove repetition.
  • Loading branch information
jniles committed Mar 29, 2018
1 parent e061abf commit 65e1f27
Show file tree
Hide file tree
Showing 6 changed files with 48 additions and 33 deletions.
3 changes: 2 additions & 1 deletion client/src/i18n/en/barcode.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
"AWAITING_INPUT" : "Waiting for Input",
"AWAITING_HTTP" : "Barcode Read! Looking up Record...",
"READ_SUCCESS" : "Found Record",
"READ_ERROR" : "Unreadable Barcode! Please enter the barcode manually.",
"READ_ERROR" : "An error occurred during lookup. Please use another method to find the record.",
"NOT_FOUND" : "Cannot find a record with that code. Please search for the document manually.",
"LOST_FOCUS" : "The barcode input has lost focus. Please click \"Read Barcode\" to ready the barcode component.",
"RESET_BUTTON" : "Read Barcode"
}
Expand Down
5 changes: 3 additions & 2 deletions client/src/i18n/fr/barcode.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
"SCAN" : "Scanner le Code à Barres",
"AWAITING_INPUT" : "En Attente d'Entrée",
"AWAITING_HTTP" : "Code à barres lu! Recherche l'enregistrement....",
"READ_SUCCESS" : "Trouvée",
"READ_ERROR" : "Code à barres illisible! Entrez le code à barres manuellement.",
"READ_SUCCESS" : "Enregistrement trouvé",
"READ_ERROR" : "Une erreur est survenue lors de la lecture du code barre.",
"NOT_FOUND" : "Enregistrement non trouvé.",
"LOST_FOCUS" : "L'entrée du code à barres a perdu le focus. Veuillez cliquer sur \"Lire le code à barres\" pour préparer le composant de code à barres.",
"RESET_BUTTON" : "Lire le code à barres"
}
Expand Down
6 changes: 4 additions & 2 deletions client/src/js/components/bhBarcodeScanner.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ function bhBarcodeScanner($timeout, $window, Barcode) {
READ_SUCCESS : 8,
READ_ERROR : 16,
LOST_FOCUS : 32,
NOT_FOUND : 64,
};

$ctrl.$onInit = () => {
Expand Down Expand Up @@ -56,8 +57,9 @@ function bhBarcodeScanner($timeout, $window, Barcode) {
$ctrl.record = record;
$ctrl.onScanCallback({ record });
})
.catch(() => {
$ctrl.currentStep = steps.READ_ERROR;
.catch(err => {
const isNotFound = (err.status === 404);
$ctrl.currentStep = isNotFound ? steps.NOT_FOUND : steps.READ_ERROR;
$ctrl.isResetButtonVisible = true;
});
};
Expand Down
6 changes: 5 additions & 1 deletion client/src/modules/templates/bhBarcodeScanner.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,13 @@ <h1 style="margin-bottom: 0;"><i class="fa fa-barcode fa-3x"></i></h1>
<i class="fa fa-check-circle-o"></i> <span translate>BARCODE.READ_SUCCESS</span> {{ $ctrl.record.reference }}
</span>

<span class="text-danger" ng-if="$ctrl.currentStep === $ctrl.steps.READ_ERROR">
<span class="text-danger" data-error="READ_ERROR" ng-if="$ctrl.currentStep === $ctrl.steps.READ_ERROR">
<i class="fa fa-danger-sign"></i> <span translate>BARCODE.READ_ERROR</span>
</span>

<span class="text-danger" data-error="NOT_FOUND" ng-if="$ctrl.currentStep === $ctrl.steps.NOT_FOUND">
<i class="fa fa-danger-sign"></i> <span translate>BARCODE.NOT_FOUND</span>
</span>
</p>

<!-- hidden barcode input -->
Expand Down
2 changes: 1 addition & 1 deletion server/lib/barcode.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ function generate(receiptIdentifier, uuid) {
// YYYYYYYY - First characters of the entity UUID
// - returns the full UUID of the entity
function reverseLookup(barcodeKey) {
const code = barcodeKey.substr(0, 2);
const code = barcodeKey.substr(0, 2).toUpperCase();
const partialUuid = barcodeKey.substr(2, barcodeKey.length);
const documentDefinition = identifiersIndex[code];

Expand Down
59 changes: 33 additions & 26 deletions test/client-unit/components/bhBarcoderScanner.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ function bhBarcodeScannerTests() {
let Barcode;
let Q;

const record = { uuid : 1, reference : 'Example Reference' };

// utilities
const find = (elm, selector) => elm[0].querySelector(selector);
const isHidden = e => angular.element(e).hasClass('ng-hide');
Expand All @@ -31,9 +29,7 @@ function bhBarcodeScannerTests() {
$scope = _$rootScope_.$new();
$timeout = _$timeout_;

// spy on the barcode service's search() method
Barcode = _BarcodeService_;
Barcode.search = chai.spy(() => $q.resolve(record));
Q = $q;

element = $compile(angular.element(template))($scope);
Expand Down Expand Up @@ -89,51 +85,62 @@ function bhBarcodeScannerTests() {
expect(isHidden(btn)).to.equal(true);
});

it('calls the barcode API when something is typed in the input', () => {
const triggerBarcodeInput = (barcodeValue) => {
const input = find(element, '#hidden-barcode-input');
const ngModel = angular.element(input).controller('ngModel');

const barcodeValue = '123456789';
ngModel.$setViewValue(barcodeValue);

// Note - the input id debounced, so we flush the timeout to force the
// ngChange to fire.
$timeout.flush();
$scope.$digest();
};

it('calls the barcode API when something is typed in the input', () => {
const record = { uuid : 1, reference : 'Example Reference' };
Barcode.search = chai.spy(() => Q.resolve(record));

const barcodeValue = '1234567';
triggerBarcodeInput(barcodeValue);

expect(Barcode.search).to.have.been.called.with(barcodeValue);
});

it('calls onScanCallback() upon a successful scan', () => {
const input = find(element, '#hidden-barcode-input');
const ngModel = angular.element(input).controller('ngModel');

const barcodeValue = '123456789';
ngModel.$setViewValue(barcodeValue);

// Note - the input id debounced, so we flush the timeout to force the
// ngChange to fire.
$timeout.flush();
$scope.$digest();

// set up mocks to succeed with a fake record
const record = { uuid : 1, reference : 'Example Reference' };
Barcode.search = chai.spy(() => Q.resolve(record));
triggerBarcodeInput('1234567');
expect($scope.callback).to.have.been.called.with(record);
});

it('does not call onScanCallback() if a failure occured', () => {
// mock a failure method
Barcode.search = () => Q.reject();
Barcode.search = chai.spy(() => Q.reject({ status : 400 }));
triggerBarcodeInput('1234567');
expect($scope.callback).to.have.not.been.called;
});

const input = find(element, '#hidden-barcode-input');
const ngModel = angular.element(input).controller('ngModel');
it('sets the NOT_FOUND step on a 404 error', () => {
// mock a 404 Not Found HTTP failure
Barcode.search = chai.spy(() => Q.reject({ status : 404 }));

const barcodeValue = '123456789';
ngModel.$setViewValue(barcodeValue);
triggerBarcodeInput('1234567');
expect($scope.callback).to.have.not.been.called;

// Note - the input id debounced, so we flush the timeout to force the
// ngChange to fire.
$timeout.flush();
$scope.$digest();
const span = find(element, '.text-danger');
expect(angular.element(span).attr('data-error')).to.equal('NOT_FOUND');
});

it('sets the READ_ERROR step on any other error', () => {
// mock a failure method
Barcode.search = chai.spy(() => Q.reject({ status : 500 }));

triggerBarcodeInput('1234567');
expect($scope.callback).to.have.not.been.called;

const span = find(element, '.text-danger');
expect(angular.element(span).attr('data-error')).to.equal('READ_ERROR');
});
}

0 comments on commit 65e1f27

Please sign in to comment.