Added scanning barcodes with a camera
This commit is contained in:
190
quagga2/quagga2-1.12.1/test/integration/README.md
Normal file
190
quagga2/quagga2-1.12.1/test/integration/README.md
Normal file
@@ -0,0 +1,190 @@
|
||||
# Integration Tests
|
||||
|
||||
## Test Infrastructure
|
||||
|
||||
This directory contains integration tests for Quagga2's barcode decoder functionality. The tests have been organized into separate files per decoder type for better maintainability.
|
||||
|
||||
### File Structure
|
||||
|
||||
- `helpers.ts` - Shared test utilities and configuration functions
|
||||
- `integration.spec.ts` - General integration tests (parallel decoding, edge cases)
|
||||
- `decoders/` - Individual decoder test files:
|
||||
- `ean.spec.ts` - EAN-13 barcode tests
|
||||
- `ean_extended.spec.ts` - EAN with supplements (EAN-2, EAN-5)
|
||||
- `ean_8.spec.ts` - EAN-8 barcode tests
|
||||
- `upc.spec.ts` - UPC-A barcode tests
|
||||
- `upc_e.spec.ts` - UPC-E barcode tests
|
||||
- `code_128.spec.ts` - Code 128 barcode tests
|
||||
- `code_39.spec.ts` - Code 39 barcode tests
|
||||
- `code_39_vin.spec.ts` - Code 39 VIN (Vehicle Identification Number) tests
|
||||
- `code_32.spec.ts` - Code 32 (Italian Pharmacode) tests
|
||||
- `code_93.spec.ts` - Code 93 barcode tests
|
||||
- `codabar.spec.ts` - Codabar barcode tests
|
||||
- `i2of5.spec.ts` - Interleaved 2 of 5 barcode tests
|
||||
- `2of5.spec.ts` - Standard 2 of 5 barcode tests
|
||||
- `external-reader.spec.ts` - Tests for external reader functionality
|
||||
|
||||
### Test Behavior
|
||||
|
||||
By default, all decoder tests **must pass in both Node and browser environments**. If a test fails, the entire test run fails, alerting developers to regressions.
|
||||
|
||||
For tests that are known to fail in specific environments, you can use environment-specific flags to mark them explicitly. **These flags are the single authoritative source** for test failure configuration.
|
||||
|
||||
### Test Helper: `it.allowFail()`
|
||||
|
||||
The `it.allowFail()` helper is used internally when a test is marked with environment-specific failure flags. When a test fails in an allowed environment, it will be marked as "pending" instead of causing the test run to fail.
|
||||
|
||||
### Marking Tests with Environment-Specific Flags
|
||||
|
||||
In the test data structures, you can mark individual test cases with explicit environment-specific failure policies:
|
||||
|
||||
- **`allowFailInNode: true`**: Test can fail in Node environment without failing the build
|
||||
- **`allowFailInBrowser: true`**: Test can fail in browser environment without failing the build
|
||||
- **Both flags**: Test can fail in both environments - set both flags explicitly
|
||||
|
||||
```typescript
|
||||
runDecoderTest('code_128', generateConfig(), [
|
||||
// This test passes everywhere - no flags needed, will fail build if it breaks
|
||||
{ 'name': 'image-001.jpg', 'result': '0001285112001000040801', format: 'code_128' },
|
||||
|
||||
// This test passes in Node but fails in browser - use allowFailInBrowser only
|
||||
{ 'name': 'image-003.jpg', 'result': '673023', format: 'code_128', allowFailInNode: true, allowFailInBrowser: true },
|
||||
|
||||
// This test fails in both environments - set BOTH flags explicitly
|
||||
{ 'name': 'failing-test.jpg', 'result': '123456', format: 'code_128', allowFailInNode: true, allowFailInBrowser: true },
|
||||
]);
|
||||
```
|
||||
|
||||
## Current Test Status
|
||||
|
||||
As of the latest update:
|
||||
|
||||
- **399 tests passing** (improved from 387, +12 tests)
|
||||
- **~54 tests pending** in both Node and browser (down from 64)
|
||||
- Tests are balanced between environments
|
||||
|
||||
### Configuration Improvements
|
||||
|
||||
Recent optimizations have significantly improved decoder accuracy:
|
||||
|
||||
**EAN-8 Decoder:**
|
||||
|
||||
- Changed `patchSize` from `'medium'` to `'large'` for better accuracy
|
||||
- Fixed: image-004 now decodes correctly with halfSample:true
|
||||
- Trade-off: image-003 now fails in browser with halfSample:false (marked with `allowFailInBrowser`)
|
||||
|
||||
**Code 39 VIN Decoder:**
|
||||
|
||||
- Increased `inputStream.size` from 1280 → 2000 → **2200** (2x the 1100px original image size)
|
||||
- Fixed: 5 images now pass (001, 003, 005, 006, 011) - improved from only 1 passing
|
||||
- Note: 6 images still fail (002, 004, 007, 008, 009, 010) even with optimal settings - marked with both allowFail flags
|
||||
- Testing revealed performance peaks around 2x: 3x and 4x scaling both perform worse (5/11 vs 10/11 passing)
|
||||
|
||||
**Interleaved 2 of 5 (i2of5) Decoder:**
|
||||
|
||||
- Set `inputStream.size` to **1375** (1.25x the 1100px original)
|
||||
- **Perfect accuracy**: All 5 test images now pass in both halfSample modes (10/10 tests)
|
||||
- Testing showed 1.25x-1.5x work well for these test images
|
||||
- Performance degrades at higher scaling: 2.5x causes complete failure in halfSample:false mode
|
||||
|
||||
**Key Insight - Upscaling Improves Detection:**
|
||||
|
||||
Contrary to conventional wisdom, **upscaling images can significantly improve barcode detection accuracy**. Testing showed:
|
||||
|
||||
- Upscaling improves detection in **both** halfSample:true and halfSample:false modes
|
||||
- Integer scaling factors (2x) provide clean pixel doubling with minimal interpolation artifacts
|
||||
- Optimal scaling varies by image content and quality, not necessarily by barcode type
|
||||
- Performance typically peaks at moderate upscaling (1.25x-2x) and degrades beyond 2.5x
|
||||
- The interpolation acts as a smoothing filter, providing more pixels per bar for the locator to analyze
|
||||
|
||||
### Decoders with Targeted Configurations
|
||||
|
||||
- **ean_8**: Uses `patchSize: 'large'` (improved accuracy)
|
||||
- **code_39_vin**: Uses `inputStream.size: 2200` (2x scaling for optimal accuracy)
|
||||
- **i2of5**: Uses `inputStream.size: 1375` and `patchSize: 'small'` (1.25x scaling, perfect 100% accuracy)
|
||||
- **code_32**: Uses `patchSize: 'large'` and `inputStream.size: 1280`
|
||||
- **code_93**: Uses `patchSize: 'large'`
|
||||
|
||||
## Running Tests
|
||||
|
||||
```bash
|
||||
# Run all integration tests in Node
|
||||
npx ts-mocha -p test/tsconfig.json test/integration/**/*.spec.ts
|
||||
|
||||
# Run all tests (including integration tests)
|
||||
npm run test:node
|
||||
|
||||
# Run browser tests (requires Cypress)
|
||||
npm run test:browser-all
|
||||
```
|
||||
|
||||
## Adding New Tests
|
||||
|
||||
When adding new decoder test cases:
|
||||
|
||||
1. Add the test data to the appropriate `runDecoderTest()` call **without** any flags
|
||||
2. Run the tests in both Node and browser environments
|
||||
3. Based on the results, add explicit flags:
|
||||
- **Passes everywhere**: Leave without flags
|
||||
- **Fails only in Node**: Add `allowFailInNode: true`
|
||||
- **Fails only in browser**: Add `allowFailInBrowser: true`
|
||||
- **Fails in both**: Add **both** `allowFailInNode: true` and `allowFailInBrowser: true`
|
||||
|
||||
Example:
|
||||
|
||||
```typescript
|
||||
runDecoderTest('my_decoder', generateConfig(), [
|
||||
// Passes everywhere - no flags needed
|
||||
{ 'name': 'working-image.jpg', 'result': '123456', format: 'my_format' },
|
||||
|
||||
// Passes in Node, fails in browser - set allowFailInBrowser only
|
||||
{ 'name': 'browser-issue.jpg', 'result': '789012', format: 'my_format', allowFailInBrowser: true },
|
||||
|
||||
// Fails in both environments - set BOTH flags explicitly
|
||||
{ 'name': 'problematic-image.jpg', 'result': '345678', format: 'my_format', allowFailInNode: true, allowFailInBrowser: true },
|
||||
]);
|
||||
```
|
||||
|
||||
## Fixing Failing Tests
|
||||
|
||||
When you fix a test that was marked with failure flags:
|
||||
|
||||
**For tests with both `allowFailInNode` and `allowFailInBrowser`:**
|
||||
|
||||
1. Remove both flags from the test
|
||||
2. Verify the test passes consistently in both Node and browser environments
|
||||
3. The test will now fail the build if it breaks in either environment
|
||||
|
||||
**For tests with `allowFailInBrowser` only:**
|
||||
|
||||
1. Fix the browser-specific issue
|
||||
2. Remove the `allowFailInBrowser: true` flag
|
||||
3. Verify the test passes in both Node and browser
|
||||
4. The test will now fail the build if it breaks in either environment
|
||||
|
||||
**For tests with `allowFailInNode` only:**
|
||||
|
||||
1. Fix the Node-specific issue
|
||||
2. Remove the `allowFailInNode: true` flag
|
||||
3. Verify the test passes in both Node and browser
|
||||
4. The test will now fail the build if it breaks in either environment
|
||||
|
||||
## Design Philosophy
|
||||
|
||||
The default behavior is "tests must pass in both environments" to catch regressions early. Environment-specific failure flags provide explicit control:
|
||||
|
||||
- **Single source of truth** - Test item flags in the spec files are the authoritative configuration
|
||||
- **Regressions are caught immediately** - If a working test breaks, the build fails
|
||||
- **Environment-specific exceptions** - Tests can be marked to allow failure in specific environments only
|
||||
- **No implicit behavior** - Flags must be set explicitly for each environment
|
||||
- **Clear intent** - Flags clearly indicate which environments have known issues
|
||||
|
||||
## Browser vs Node Differences
|
||||
|
||||
CI runs integration tests in **both Cypress (browser) and ts-node (Node.js)**. Some tests behave differently between these environments due to differences in image processing (Browser uses native Canvas API, Node uses the `canvas` package).
|
||||
|
||||
### Configuration Trade-offs
|
||||
|
||||
When optimizing decoder configurations for accuracy, some changes may improve one test while causing another to fail. These trade-offs are documented with comments in the decoder spec files and marked with appropriate failure flags.
|
||||
|
||||
Example: Changing EAN-8's `patchSize` to `'large'` fixed image-004 but caused image-003 to fail in the browser environment. The net result is still positive (more tests passing overall), and the failure is explicitly marked.
|
||||
@@ -0,0 +1,25 @@
|
||||
import { runDecoderTestBothHalfSample, generateConfig } from '../helpers';
|
||||
|
||||
describe('2 of 5 Decoder Tests', () => {
|
||||
const twoOf5TestSet = [
|
||||
{ 'name': 'image-001.jpg', 'result': '9577149002', format: '2of5' },
|
||||
{ 'name': 'image-002.jpg', 'result': '9577149002', format: '2of5' },
|
||||
{ 'name': 'image-003.jpg', 'result': '5776158811', format: '2of5' },
|
||||
{ 'name': 'image-004.jpg', 'result': '0463381455', format: '2of5' },
|
||||
{ 'name': 'image-005.jpg', 'result': '3261594101', format: '2of5', allowFailInNode: true, allowFailInBrowser: true },
|
||||
{ 'name': 'image-006.jpg', 'result': '3261594101', format: '2of5', allowFailInNode: true, allowFailInBrowser: true },
|
||||
{ 'name': 'image-007.jpg', 'result': '3261594101', format: '2of5' },
|
||||
{ 'name': 'image-008.jpg', 'result': '6730705801', format: '2of5' },
|
||||
{ 'name': 'image-009.jpg', 'result': '5776158811', format: '2of5' },
|
||||
{ 'name': 'image-010.jpg', 'result': '5776158811', format: '2of5' },
|
||||
];
|
||||
runDecoderTestBothHalfSample('2of5', (halfSample) => generateConfig({
|
||||
inputStream: { size: 800, singleChannel: false },
|
||||
locator: {
|
||||
halfSample,
|
||||
},
|
||||
decoder: {
|
||||
readers: ['2of5_reader'],
|
||||
},
|
||||
}), twoOf5TestSet);
|
||||
});
|
||||
@@ -0,0 +1,24 @@
|
||||
import { runDecoderTestBothHalfSample, generateConfig } from '../helpers';
|
||||
|
||||
describe('Codabar Decoder Tests', () => {
|
||||
const codabarTestSet = [
|
||||
{ 'name': 'image-001.jpg', 'result': 'A10/53+17-70D', format: 'codabar' },
|
||||
{ 'name': 'image-002.jpg', 'result': 'B546745735B', format: 'codabar' },
|
||||
{ 'name': 'image-003.jpg', 'result': 'C$399.95A', format: 'codabar' },
|
||||
{ 'name': 'image-004.jpg', 'result': 'B546745735B', format: 'codabar' },
|
||||
{ 'name': 'image-005.jpg', 'result': 'C$399.95A', format: 'codabar' },
|
||||
{ 'name': 'image-006.jpg', 'result': 'B546745735B', format: 'codabar' },
|
||||
{ 'name': 'image-007.jpg', 'result': 'C$399.95A', format: 'codabar' },
|
||||
{ 'name': 'image-008.jpg', 'result': 'A16:9/4:3/3:2D', format: 'codabar', allowFailInNode: true, allowFailInBrowser: true },
|
||||
{ 'name': 'image-009.jpg', 'result': 'C$399.95A', format: 'codabar' },
|
||||
{ 'name': 'image-010.jpg', 'result': 'C$399.95A', format: 'codabar' },
|
||||
];
|
||||
runDecoderTestBothHalfSample('codabar', (halfSample) => generateConfig({
|
||||
locator: {
|
||||
halfSample,
|
||||
},
|
||||
decoder: {
|
||||
readers: ['codabar_reader']
|
||||
}
|
||||
}), codabarTestSet);
|
||||
});
|
||||
@@ -0,0 +1,44 @@
|
||||
import { runDecoderTestBothHalfSample, generateConfig } from '../helpers';
|
||||
|
||||
describe('Code 128 Decoder Tests', () => {
|
||||
// Note: FNC1 characters are represented as ASCII 29 (Group Separator, \x1D or \u001d)
|
||||
// These are used in GS1-128 barcodes as field separators
|
||||
const FNC1 = String.fromCharCode(29);
|
||||
const code128TestSet = [
|
||||
{ 'name': 'image-001.jpg', 'result': '0001285112001000040801', format: 'code_128' },
|
||||
{ 'name': 'image-002.jpg', 'result': 'FANAVF14617104', format: 'code_128' },
|
||||
{ 'name': 'image-003.jpg', 'result': '673023', format: 'code_128', allowFailInNode: true, allowFailInBrowser: true },
|
||||
{ 'name': 'image-004.jpg', 'result': '010210150301625334', format: 'code_128', allowFailInNode: true, allowFailInBrowser: true },
|
||||
{ 'name': 'image-005.jpg', 'result': '419055603900009001012999', format: 'code_128' },
|
||||
{ 'name': 'image-006.jpg', 'result': '419055603900009001012999', format: 'code_128' },
|
||||
// GS1-128 barcode with FNC1 characters as field separators
|
||||
{ 'name': 'image-007.jpg', 'result': `${FNC1}42095747${FNC1}9499907123456123456781`, format: 'code_128' },
|
||||
{ 'name': 'image-008.jpg', 'result': '1020185021797280784055', format: 'code_128' },
|
||||
{ 'name': 'image-009.jpg', 'result': '0001285112001000040801', format: 'code_128' },
|
||||
{ 'name': 'image-010.jpg', 'result': '673023', format: 'code_128' },
|
||||
// TODO: need to implement having different inputStream parameters to be able to
|
||||
// read this one -- it works only with inputStream size set to 1600 presently, but
|
||||
// other samples break at that high a size.
|
||||
// { name: 'image-011.png', result: '33c64780-a9c0-e92a-820c-fae7011c11e2' },
|
||||
// GS1-128 barcodes from issue #390 - real-world food packaging barcodes
|
||||
// image-012 works with halfSample: false, but not with halfSample: true
|
||||
{ 'name': 'image-012.jpg', 'result': '01906641589574681121102531020003402152731515', format: 'code_128', allowFailInNode: true, allowFailInBrowser: true },
|
||||
// image-013 and image-014 require higher resolution settings to decode properly
|
||||
// According to issue #390, image-013 needs size: 1280, patchSize: 'small'
|
||||
// and image-014 needs size: 1600, patchSize: 'large'
|
||||
{ 'name': 'image-013.jpg', 'result': '', format: 'code_128', allowFailInNode: true, allowFailInBrowser: true },
|
||||
{ 'name': 'image-014.jpg', 'result': '', format: 'code_128', allowFailInNode: true, allowFailInBrowser: true },
|
||||
];
|
||||
runDecoderTestBothHalfSample('code_128', (halfSample) => generateConfig({
|
||||
inputStream: {
|
||||
size: 800,
|
||||
singleChannel: false,
|
||||
},
|
||||
locator: {
|
||||
halfSample,
|
||||
},
|
||||
decoder: {
|
||||
readers: ['code_128_reader'],
|
||||
},
|
||||
}), code128TestSet);
|
||||
});
|
||||
@@ -0,0 +1,29 @@
|
||||
import { runDecoderTestBothHalfSample, generateConfig } from '../helpers';
|
||||
|
||||
describe('Code 32 Decoder Tests', () => {
|
||||
const code32TestSet = [
|
||||
{ name: 'image-1.jpg', result: 'A123456788', format: 'code_32_reader' },
|
||||
{ name: 'image-2.jpg', result: 'A931028462', format: 'code_32_reader', allowFailInNode: true },
|
||||
{ name: 'image-3.jpg', result: 'A931028462', format: 'code_32_reader', allowFailInNode: true },
|
||||
{ name: 'image-4.jpg', result: 'A935776043', format: 'code_32_reader' },
|
||||
{ name: 'image-5.jpg', result: 'A935776043', format: 'code_32_reader' },
|
||||
{ name: 'image-6.jpg', result: 'A012745182', format: 'code_32_reader', allowFailInNode: true, allowFailInBrowser: true },
|
||||
{ name: 'image-7.jpg', result: 'A029651039', format: 'code_32_reader', allowFailInNode: true },
|
||||
{ name: 'image-8.jpg', result: 'A029651039', format: 'code_32_reader', allowFailInNode: true, allowFailInBrowser: true },
|
||||
{ name: 'image-9.jpg', result: 'A015896018', format: 'code_32_reader' },
|
||||
{ name: 'image-10.jpg', result: 'A015896018', format: 'code_32_reader' },
|
||||
];
|
||||
runDecoderTestBothHalfSample('code_32', (halfSample) => generateConfig({
|
||||
inputStream: {
|
||||
size: 1280,
|
||||
},
|
||||
locator: {
|
||||
patchSize: 'large',
|
||||
halfSample,
|
||||
},
|
||||
numOfWorkers: 4,
|
||||
decoder: {
|
||||
readers: ['code_32_reader']
|
||||
}
|
||||
}), code32TestSet);
|
||||
});
|
||||
@@ -0,0 +1,25 @@
|
||||
import { runDecoderTestBothHalfSample, generateConfig } from '../helpers';
|
||||
|
||||
describe('Code 39 Decoder Tests', () => {
|
||||
const code39TestSet = [
|
||||
{ 'name': 'image-001.jpg', 'result': 'B3% $DAD$', format: 'code_39' },
|
||||
{ 'name': 'image-003.jpg', 'result': 'CODE39', format: 'code_39' },
|
||||
{ 'name': 'image-004.jpg', 'result': 'QUAGGAJS', format: 'code_39' },
|
||||
{ 'name': 'image-005.jpg', 'result': 'CODE39', format: 'code_39', allowFailInNode: true, allowFailInBrowser: true },
|
||||
{ 'name': 'image-006.jpg', 'result': '2/4-8/16-32', format: 'code_39' },
|
||||
{ 'name': 'image-007.jpg', 'result': '2/4-8/16-32', format: 'code_39' },
|
||||
{ 'name': 'image-008.jpg', 'result': 'CODE39', format: 'code_39' },
|
||||
{ 'name': 'image-009.jpg', 'result': '2/4-8/16-32', format: 'code_39' },
|
||||
// TODO: image 10 in this set appears to be dependent upon #191
|
||||
{ 'name': 'image-010.jpg', 'result': 'CODE39', format: 'code_39' },
|
||||
{ 'name': 'image-011.jpg', 'result': '4', format: 'code_39', allowFailInNode: true, allowFailInBrowser: true },
|
||||
];
|
||||
runDecoderTestBothHalfSample('code_39', (halfSample) => generateConfig({
|
||||
locator: {
|
||||
halfSample,
|
||||
},
|
||||
decoder: {
|
||||
readers: ['code_39_reader'],
|
||||
}
|
||||
}), code39TestSet);
|
||||
});
|
||||
@@ -0,0 +1,29 @@
|
||||
import { runDecoderTestBothHalfSample, generateConfig } from '../helpers';
|
||||
|
||||
describe('Code 39 VIN Decoder Tests', () => {
|
||||
const code39VinTestSet = [
|
||||
{ name: 'image-001.jpg', result: '2HGFG1B86BH501831', format: 'code_39_vin' },
|
||||
{ name: 'image-002.jpg', result: 'JTDKB20U887718156', format: 'code_39_vin', allowFailInNode: true, allowFailInBrowser: true },
|
||||
{ name: 'image-003.jpg', result: 'JM1BK32G071773697', format: 'code_39_vin' },
|
||||
{ name: 'image-004.jpg', result: 'WDBTK75G94T028954', format: 'code_39_vin', allowFailInNode: true, allowFailInBrowser: true },
|
||||
{ name: 'image-005.jpg', result: '3VW2K7AJ9EM381173', format: 'code_39_vin' },
|
||||
{ name: 'image-006.jpg', result: 'JM1BL1H4XA1335663', format: 'code_39_vin' },
|
||||
{ name: 'image-007.jpg', result: 'JHMGE8H42AS021233', format: 'code_39_vin', allowFailInNode: true, allowFailInBrowser: true },
|
||||
{ name: 'image-008.jpg', result: 'WMEEJ3BA4DK652562', format: 'code_39_vin', allowFailInNode: true, allowFailInBrowser: true },
|
||||
{ name: 'image-009.jpg', result: 'WMEEJ3BA4DK652562', format: 'code_39_vin', allowFailInNode: true, allowFailInBrowser: true }, //yes, 8 and 9 are same barcodes, different images slightly
|
||||
{ name: 'image-010.jpg', result: 'WMEEJ3BA4DK652562', format: 'code_39_vin', allowFailInNode: true, allowFailInBrowser: true }, // 10 also
|
||||
{ name: 'image-011.jpg', result: '5FNRL38488B411196', format: 'code_39_vin' },
|
||||
];
|
||||
runDecoderTestBothHalfSample('code_39_vin', (halfSample) => generateConfig({
|
||||
inputStream: {
|
||||
size: 2200, // 2x scaling (from 1100x658) provides optimal detection accuracy
|
||||
sequence: false,
|
||||
},
|
||||
locator: {
|
||||
halfSample,
|
||||
},
|
||||
decoder: {
|
||||
readers: ['code_39_vin_reader'],
|
||||
},
|
||||
}), code39VinTestSet);
|
||||
});
|
||||
@@ -0,0 +1,27 @@
|
||||
import { runDecoderTestBothHalfSample, generateConfig } from '../helpers';
|
||||
|
||||
describe('Code 93 Decoder Tests', () => {
|
||||
const code93TestSet = [
|
||||
{ 'name': 'image-001.jpg', 'result': 'WIWV8ETQZ1', format: 'code_93' },
|
||||
{ 'name': 'image-002.jpg', 'result': 'EH3C-%GU23RK3', format: 'code_93' },
|
||||
{ 'name': 'image-003.jpg', 'result': 'O308SIHQOXN5SA/PJ', format: 'code_93', allowFailInNode: true, allowFailInBrowser: true },
|
||||
{ 'name': 'image-004.jpg', 'result': 'DG7Q$TV8JQ/EN', format: 'code_93', allowFailInNode: true, allowFailInBrowser: true },
|
||||
{ 'name': 'image-005.jpg', 'result': 'DG7Q$TV8JQ/EN', format: 'code_93', allowFailInNode: true, allowFailInBrowser: true },
|
||||
{ 'name': 'image-006.jpg', 'result': 'O308SIHQOXN5SA/PJ', format: 'code_93' },
|
||||
{ 'name': 'image-007.jpg', 'result': 'VOFD1DB5A.1F6QU', format: 'code_93', allowFailInNode: true, allowFailInBrowser: true },
|
||||
{ 'name': 'image-008.jpg', 'result': 'WIWV8ETQZ1', format: 'code_93', allowFailInNode: true, allowFailInBrowser: true },
|
||||
{ 'name': 'image-009.jpg', 'result': '4SO64P4X8 U4YUU1T-', format: 'code_93' },
|
||||
{ 'name': 'image-010.jpg', 'result': '4SO64P4X8 U4YUU1T-', format: 'code_93', allowFailInNode: true, allowFailInBrowser: true },
|
||||
{ 'name': 'image-011.jpg', result: '11169', format: 'code_93' },
|
||||
];
|
||||
runDecoderTestBothHalfSample('code_93', (halfSample) => generateConfig({
|
||||
inputStream: { size: 800, singleChannel: false },
|
||||
locator: {
|
||||
patchSize: 'large',
|
||||
halfSample,
|
||||
},
|
||||
decoder: {
|
||||
readers: ['code_93_reader'],
|
||||
},
|
||||
}), code93TestSet);
|
||||
});
|
||||
17
quagga2/quagga2-1.12.1/test/integration/decoders/ean.spec.ts
Normal file
17
quagga2/quagga2-1.12.1/test/integration/decoders/ean.spec.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { runDecoderTestBothHalfSample, generateConfig } from '../helpers';
|
||||
|
||||
describe('EAN Decoder Tests', () => {
|
||||
const eanTestSet = [
|
||||
{ 'name': 'image-001.jpg', 'result': '3574660239843', format: 'ean_13' },
|
||||
{ 'name': 'image-002.jpg', 'result': '8032754490297', format: 'ean_13' },
|
||||
{ 'name': 'image-004.jpg', 'result': '9002233139084', format: 'ean_13' },
|
||||
{ 'name': 'image-003.jpg', 'result': '4006209700068', format: 'ean_13' },
|
||||
{ 'name': 'image-005.jpg', 'result': '8004030044005', format: 'ean_13' },
|
||||
{ 'name': 'image-006.jpg', 'result': '4003626011159', format: 'ean_13' },
|
||||
{ 'name': 'image-007.jpg', 'result': '2111220009686', format: 'ean_13' },
|
||||
{ 'name': 'image-008.jpg', 'result': '9000275609022', format: 'ean_13' },
|
||||
{ 'name': 'image-009.jpg', 'result': '9004593978587', format: 'ean_13' },
|
||||
{ 'name': 'image-010.jpg', 'result': '9002244845578', format: 'ean_13' },
|
||||
];
|
||||
runDecoderTestBothHalfSample('ean', (halfSample) => generateConfig({ locator: { halfSample } }), eanTestSet);
|
||||
});
|
||||
@@ -0,0 +1,25 @@
|
||||
import { runDecoderTestBothHalfSample, generateConfig } from '../helpers';
|
||||
|
||||
describe('EAN-8 Decoder Tests', () => {
|
||||
const ean8TestSet = [
|
||||
{ 'name': 'image-001.jpg', 'result': '42191605', format: 'ean_8' },
|
||||
{ 'name': 'image-002.jpg', 'result': '42191605', format: 'ean_8' },
|
||||
{ 'name': 'image-003.jpg', 'result': '90311208', format: 'ean_8', allowFailInBrowser: true },
|
||||
{ 'name': 'image-004.jpg', 'result': '24057257', format: 'ean_8' },
|
||||
// {"name": "image-005.jpg", "result": "90162602"},
|
||||
{ 'name': 'image-006.jpg', 'result': '24036153', format: 'ean_8' },
|
||||
// {"name": "image-007.jpg", "result": "42176817"},
|
||||
{ 'name': 'image-008.jpg', 'result': '42191605', format: 'ean_8' },
|
||||
{ 'name': 'image-009.jpg', 'result': '42242215', format: 'ean_8', allowFailInNode: true, allowFailInBrowser: true },
|
||||
{ 'name': 'image-010.jpg', 'result': '42184799', format: 'ean_8' },
|
||||
];
|
||||
runDecoderTestBothHalfSample('ean_8', (halfSample) => generateConfig({
|
||||
locator: {
|
||||
patchSize: 'large',
|
||||
halfSample,
|
||||
},
|
||||
decoder: {
|
||||
readers: ['ean_8_reader']
|
||||
}
|
||||
}), ean8TestSet);
|
||||
});
|
||||
@@ -0,0 +1,39 @@
|
||||
import { runDecoderTestBothHalfSample, generateConfig } from '../helpers';
|
||||
|
||||
describe('EAN Extended Decoder Tests', () => {
|
||||
// Note: The main result's format is 'ean_13' (the parent barcode format).
|
||||
// The supplement's format is available in result.codeResult.supplement.format
|
||||
// and will correctly be 'ean_2' or 'ean_5' depending on the supplement type.
|
||||
const eanExtendedTestSet = [
|
||||
{ 'name': 'image-001.jpg', 'result': '900437801102701', format: 'ean_13', allowFailInNode: true, allowFailInBrowser: true },
|
||||
{ 'name': 'image-002.jpg', 'result': '419871600890101', format: 'ean_13' },
|
||||
{ 'name': 'image-003.jpg', 'result': '419871600890101', format: 'ean_13', allowFailInNode: true, allowFailInBrowser: true },
|
||||
{ 'name': 'image-004.jpg', 'result': '978054466825652495', format: 'ean_13' },
|
||||
{ 'name': 'image-005.jpg', 'result': '419664190890712', format: 'ean_13', allowFailInNode: true, allowFailInBrowser: true },
|
||||
{ 'name': 'image-006.jpg', 'result': '412056690699101', format: 'ean_13', allowFailInNode: true, allowFailInBrowser: true },
|
||||
{ 'name': 'image-007.jpg', 'result': '419204531290601', format: 'ean_13' },
|
||||
{ 'name': 'image-008.jpg', 'result': '419871600890101', format: 'ean_13' },
|
||||
{ 'name': 'image-009.jpg', 'result': '978054466825652495', format: 'ean_13' },
|
||||
{ 'name': 'image-010.jpg', 'result': '900437801102701', format: 'ean_13' },
|
||||
];
|
||||
runDecoderTestBothHalfSample('ean_extended', (halfSample) => generateConfig({
|
||||
inputStream: {
|
||||
size: 800,
|
||||
singleChannel: false,
|
||||
},
|
||||
locator: {
|
||||
halfSample,
|
||||
},
|
||||
decoder: {
|
||||
readers: [{
|
||||
format: 'ean_reader',
|
||||
config: {
|
||||
supplements: [
|
||||
'ean_5_reader',
|
||||
'ean_2_reader',
|
||||
],
|
||||
},
|
||||
}],
|
||||
},
|
||||
}), eanExtendedTestSet);
|
||||
});
|
||||
@@ -0,0 +1,70 @@
|
||||
import Quagga from '../../../src/quagga';
|
||||
import { expect } from 'chai';
|
||||
import { it } from '../helpers';
|
||||
|
||||
describe('EAN Supplement Format Tests', () => {
|
||||
// Test that verifies the supplement format is correctly returned as 'ean_2' or 'ean_5'
|
||||
// rather than inheriting the parent 'ean_13' format
|
||||
|
||||
const isBrowser = typeof window !== 'undefined';
|
||||
const fixturePrefix = isBrowser ? '/' : '';
|
||||
|
||||
const baseConfig = {
|
||||
inputStream: {
|
||||
size: 800,
|
||||
singleChannel: false,
|
||||
},
|
||||
locator: {
|
||||
patchSize: 'medium' as const,
|
||||
halfSample: false,
|
||||
},
|
||||
numOfWorkers: 0,
|
||||
decoder: {
|
||||
readers: [{
|
||||
format: 'ean_reader',
|
||||
config: {
|
||||
supplements: [
|
||||
'ean_5_reader',
|
||||
'ean_2_reader',
|
||||
],
|
||||
},
|
||||
}],
|
||||
},
|
||||
};
|
||||
|
||||
it('should return ean_2 format for 2-digit supplement', async function() {
|
||||
this.timeout(30000);
|
||||
|
||||
const config = {
|
||||
...baseConfig,
|
||||
src: `${fixturePrefix}test/fixtures/ean_extended/image-002.jpg`, // EAN-13 with 2-digit supplement
|
||||
};
|
||||
|
||||
const result = await Quagga.decodeSingle(config);
|
||||
|
||||
expect(result).to.be.an('Object');
|
||||
expect(result.codeResult).to.be.an('Object');
|
||||
expect(result.codeResult.format).to.equal('ean_13');
|
||||
expect(result.codeResult.supplement).to.be.an('Object');
|
||||
expect(result.codeResult.supplement.format).to.equal('ean_2');
|
||||
expect(result.codeResult.supplement.code).to.equal('01');
|
||||
});
|
||||
|
||||
it('should return ean_5 format for 5-digit supplement', async function() {
|
||||
this.timeout(30000);
|
||||
|
||||
const config = {
|
||||
...baseConfig,
|
||||
src: `${fixturePrefix}test/fixtures/ean_extended/image-004.jpg`, // EAN-13 with 5-digit supplement
|
||||
};
|
||||
|
||||
const result = await Quagga.decodeSingle(config);
|
||||
|
||||
expect(result).to.be.an('Object');
|
||||
expect(result.codeResult).to.be.an('Object');
|
||||
expect(result.codeResult.format).to.equal('ean_13');
|
||||
expect(result.codeResult.supplement).to.be.an('Object');
|
||||
expect(result.codeResult.supplement.format).to.equal('ean_5');
|
||||
expect(result.codeResult.supplement.code).to.equal('52495');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,21 @@
|
||||
import { runDecoderTestBothHalfSample, generateConfig } from '../helpers';
|
||||
|
||||
describe('Interleaved 2 of 5 Decoder Tests', () => {
|
||||
const i2of5TestSet = [
|
||||
{ 'name': 'image-001.jpg', 'result': '2167361334', format: 'i2of5' },
|
||||
{ 'name': 'image-002.jpg', 'result': '2167361334', format: 'i2of5' },
|
||||
{ 'name': 'image-003.jpg', 'result': '2167361334', format: 'i2of5' },
|
||||
{ 'name': 'image-004.jpg', 'result': '2167361334', format: 'i2of5' },
|
||||
{ 'name': 'image-005.jpg', 'result': '2167361334', format: 'i2of5' },
|
||||
];
|
||||
runDecoderTestBothHalfSample('i2of5', (halfSample) => generateConfig({
|
||||
inputStream: { size: 1375, singleChannel: false }, // 1.25x scaling (from 1100x658) provides optimal detection
|
||||
locator: {
|
||||
patchSize: 'small',
|
||||
halfSample,
|
||||
},
|
||||
decoder: {
|
||||
readers: ['i2of5_reader'],
|
||||
},
|
||||
}), i2of5TestSet);
|
||||
});
|
||||
@@ -0,0 +1,129 @@
|
||||
import { runDecoderTestBothHalfSample, runNoCodeTest, generateConfig } from '../helpers';
|
||||
|
||||
describe('Pharmacode Decoder Tests', () => {
|
||||
// Synthetic test images with known values
|
||||
// Note: Tests only run with halfSample: false currently work reliably
|
||||
// halfSample: true causes bar width detection issues for some images
|
||||
const pharmacodeTestSet = [
|
||||
{ 'name': 'image-001.jpg', 'result': '3', format: 'pharmacode' },
|
||||
{ 'name': 'image-002.jpg', 'result': '7', format: 'pharmacode' },
|
||||
{ 'name': 'image-003.jpg', 'result': '12', format: 'pharmacode' },
|
||||
{ 'name': 'image-004.jpg', 'result': '15', format: 'pharmacode' },
|
||||
{ 'name': 'image-005.jpg', 'result': '64', format: 'pharmacode' },
|
||||
{ 'name': 'image-006.jpg', 'result': '100', format: 'pharmacode' },
|
||||
{ 'name': 'image-007.jpg', 'result': '255', format: 'pharmacode' },
|
||||
{ 'name': 'image-008.jpg', 'result': '755', format: 'pharmacode' },
|
||||
{ 'name': 'image-009.jpg', 'result': '1000', format: 'pharmacode' },
|
||||
{ 'name': 'image-010.jpg', 'result': '4096', format: 'pharmacode' },
|
||||
{ 'name': 'image-011.jpg', 'result': '12345', format: 'pharmacode' },
|
||||
{ 'name': 'image-012.jpg', 'result': '65535', format: 'pharmacode' },
|
||||
];
|
||||
|
||||
// Real-world test images added by @ericblade
|
||||
// Images 013, 014, 018 contain Pharmacode value 123456
|
||||
// Image 017 contains multiple barcodes: 4174 and 3715
|
||||
// Images 015, 016 have unknown values
|
||||
|
||||
const pharmacodeRealWorldPositiveTestSet = [
|
||||
{ 'name': 'image-013.png', 'result': '123456', format: 'pharmacode' },
|
||||
// image-014 is two-track pharmacode, not supported at the moment -- maybe not ever depending on difficulty level
|
||||
// { 'name': 'image-014.png', 'result': '123456', format: 'pharmacode' },
|
||||
];
|
||||
|
||||
// image-018 requires a constrained scan window to avoid false positives elsewhere in the frame
|
||||
// still working out how to fix the false positive from the "orange and white" barcode.
|
||||
const pharmacodeRealWorldAreaConstrainedTestSet = [
|
||||
{ 'name': 'image-018.png', 'result': '123456', format: 'pharmacode' },
|
||||
];
|
||||
|
||||
// Images intentionally expected to decode nothing (should succeed with empty result)
|
||||
const pharmacodeRealWorldNoCodeTestSet = [
|
||||
{ 'name': 'image-015.png', 'result': '', format: 'pharmacode' },
|
||||
{ 'name': 'image-016.png', 'result': '', format: 'pharmacode' },
|
||||
{ 'name': 'image-016-sheared.png', 'result': '', format: 'pharmacode' },
|
||||
{ 'name': 'image-017.png', 'result': '', format: 'pharmacode' },
|
||||
];
|
||||
|
||||
// Cross-barcode rejection: i2of5 images should be rejected by pharmacode reader
|
||||
// This ensures the pharmacode reader doesn't accidentally decode other barcode types
|
||||
const pharmacodeCrossBarcodeRejectionTestSet = [
|
||||
{ 'name': 'image-011.jpg', 'result': '', format: 'pharmacode' },
|
||||
];
|
||||
|
||||
// Use locate: false since test images are synthetically generated and pre-cropped to contain only the barcode (location detection not required)
|
||||
runDecoderTestBothHalfSample('pharmacode set 1', (halfSample) => generateConfig({
|
||||
locate: false,
|
||||
locator: {
|
||||
halfSample,
|
||||
},
|
||||
decoder: {
|
||||
readers: ['pharmacode_reader']
|
||||
}
|
||||
}), pharmacodeTestSet, 'pharmacode');
|
||||
|
||||
runDecoderTestBothHalfSample('pharmacode set 2', (halfSample) => generateConfig({
|
||||
locate: false,
|
||||
inputStream: {
|
||||
size: 800,
|
||||
},
|
||||
locator: {
|
||||
halfSample,
|
||||
patchSize: 'large',
|
||||
},
|
||||
decoder: {
|
||||
readers: ['pharmacode_reader']
|
||||
}
|
||||
}), pharmacodeRealWorldPositiveTestSet, 'pharmacode');
|
||||
|
||||
// Dedicated run for image-018 with a narrowed search area (bottom 50%) - top 50% has an unreadable code
|
||||
runDecoderTestBothHalfSample('pharmacode area constrained', (halfSample) => generateConfig({
|
||||
locate: false,
|
||||
inputStream: {
|
||||
size: 800,
|
||||
area: {
|
||||
top: '50%',
|
||||
},
|
||||
},
|
||||
locator: {
|
||||
halfSample,
|
||||
patchSize: 'large',
|
||||
},
|
||||
decoder: {
|
||||
readers: ['pharmacode_reader']
|
||||
}
|
||||
}), pharmacodeRealWorldAreaConstrainedTestSet, 'pharmacode');
|
||||
|
||||
// Explicitly validate that certain images decode to nothing (empty barcodes array)
|
||||
[true, false].forEach((halfSample) => {
|
||||
runNoCodeTest(`pharmacode SHOULD NOT DECODE halfSample:${halfSample}`, generateConfig({
|
||||
locate: false,
|
||||
inputStream: {
|
||||
size: 800,
|
||||
},
|
||||
locator: {
|
||||
halfSample,
|
||||
patchSize: 'large',
|
||||
},
|
||||
decoder: {
|
||||
readers: ['pharmacode_reader']
|
||||
}
|
||||
}), pharmacodeRealWorldNoCodeTestSet, 'pharmacode');
|
||||
});
|
||||
|
||||
// Cross-barcode rejection: Pharmacode reader should reject other barcode types (e.g., i2of5)
|
||||
[true, false].forEach((halfSample) => {
|
||||
runNoCodeTest(`pharmacode rejects i2of5 barcodes halfSample:${halfSample}`, generateConfig({
|
||||
locate: false,
|
||||
inputStream: {
|
||||
size: 800,
|
||||
},
|
||||
locator: {
|
||||
halfSample,
|
||||
patchSize: 'large',
|
||||
},
|
||||
decoder: {
|
||||
readers: ['pharmacode_reader']
|
||||
}
|
||||
}), pharmacodeCrossBarcodeRejectionTestSet, 'i2of5');
|
||||
});
|
||||
});
|
||||
24
quagga2/quagga2-1.12.1/test/integration/decoders/upc.spec.ts
Normal file
24
quagga2/quagga2-1.12.1/test/integration/decoders/upc.spec.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { runDecoderTestBothHalfSample, generateConfig } from '../helpers';
|
||||
|
||||
describe('UPC-A Decoder Tests', () => {
|
||||
const upcTestSet = [
|
||||
{ 'name': 'image-001.jpg', 'result': '882428015268', format: 'upc_a' },
|
||||
{ 'name': 'image-002.jpg', 'result': '882428015268', format: 'upc_a' },
|
||||
{ 'name': 'image-003.jpg', 'result': '882428015084', format: 'upc_a' },
|
||||
{ 'name': 'image-004.jpg', 'result': '882428015343', format: 'upc_a' },
|
||||
{ 'name': 'image-005.jpg', 'result': '882428015343', format: 'upc_a' },
|
||||
{ 'name': 'image-006.jpg', 'result': '882428015046', format: 'upc_a', allowFailInNode: true, allowFailInBrowser: true },
|
||||
{ 'name': 'image-007.jpg', 'result': '882428015084', format: 'upc_a' },
|
||||
{ 'name': 'image-008.jpg', 'result': '882428015046', format: 'upc_a' },
|
||||
{ 'name': 'image-009.jpg', 'result': '039047013551', format: 'upc_a' },
|
||||
{ 'name': 'image-010.jpg', 'result': '039047013551', format: 'upc_a', allowFailInNode: true, allowFailInBrowser: true },
|
||||
];
|
||||
runDecoderTestBothHalfSample('upc', (halfSample) => generateConfig({
|
||||
locator: {
|
||||
halfSample,
|
||||
},
|
||||
decoder: {
|
||||
readers: ['upc_reader']
|
||||
}
|
||||
}), upcTestSet);
|
||||
});
|
||||
@@ -0,0 +1,24 @@
|
||||
import { runDecoderTestBothHalfSample, generateConfig } from '../helpers';
|
||||
|
||||
describe('UPC-E Decoder Tests', () => {
|
||||
const upcETestSet = [
|
||||
{ 'name': 'image-001.jpg', 'result': '04965802', format: 'upc_e', allowFailInNode: true, allowFailInBrowser: true },
|
||||
{ 'name': 'image-002.jpg', 'result': '04965802', format: 'upc_e' },
|
||||
{ 'name': 'image-003.jpg', 'result': '03897425', format: 'upc_e' },
|
||||
{ 'name': 'image-004.jpg', 'result': '05096893', format: 'upc_e' },
|
||||
{ 'name': 'image-005.jpg', 'result': '05096893', format: 'upc_e', allowFailInNode: true, allowFailInBrowser: true },
|
||||
{ 'name': 'image-006.jpg', 'result': '05096893', format: 'upc_e' },
|
||||
{ 'name': 'image-007.jpg', 'result': '03897425', format: 'upc_e', allowFailInNode: true, allowFailInBrowser: true },
|
||||
{ 'name': 'image-008.jpg', 'result': '01264904', format: 'upc_e', allowFailInNode: true, allowFailInBrowser: true },
|
||||
{ 'name': 'image-009.jpg', 'result': '01264904', format: 'upc_e', allowFailInNode: true, allowFailInBrowser: true },
|
||||
{ 'name': 'image-010.jpg', 'result': '01264904', format: 'upc_e', allowFailInNode: true, allowFailInBrowser: true },
|
||||
];
|
||||
runDecoderTestBothHalfSample('upc_e', (halfSample) => generateConfig({
|
||||
locator: {
|
||||
halfSample,
|
||||
},
|
||||
decoder: {
|
||||
readers: ['upc_e_reader']
|
||||
}
|
||||
}), upcETestSet);
|
||||
});
|
||||
@@ -0,0 +1,48 @@
|
||||
import Quagga from '../../src/quagga';
|
||||
import TestExternalCode128Reader from '../../src/reader/test_external_code_128_reader';
|
||||
import { runDecoderTestBothHalfSample, generateConfig } from './helpers';
|
||||
|
||||
describe('External Reader Test, using test external code_128 reader', () => {
|
||||
// NOTE: This test demonstrates the external reader plugin API.
|
||||
// There is a known issue where external readers may fail intermittently in TypeScript
|
||||
// test environments. The .allowFail mechanism handles this by skipping failing tests
|
||||
// rather than failing the build. This issue does not occur in production (compiled code).
|
||||
// External readers work correctly in production (compiled code).
|
||||
describe('works', () => {
|
||||
before(() => {
|
||||
Quagga.registerReader('external_code_128_reader', TestExternalCode128Reader);
|
||||
});
|
||||
// Note: FNC1 characters are represented as ASCII 29 (Group Separator, \x1D or \u001d)
|
||||
// These are used in GS1-128 barcodes as field separators
|
||||
const FNC1 = String.fromCharCode(29);
|
||||
const externalCode128TestSet = [
|
||||
{ 'name': 'image-001.jpg', 'result': '0001285112001000040801', format: 'code_128' },
|
||||
{ 'name': 'image-002.jpg', 'result': 'FANAVF14617104', format: 'code_128' },
|
||||
{ 'name': 'image-003.jpg', 'result': '673023', format: 'code_128' },
|
||||
{ 'name': 'image-004.jpg', 'result': '010210150301625334', format: 'code_128', allowFailInNode: true, allowFailInBrowser: true },
|
||||
{ 'name': 'image-005.jpg', 'result': '419055603900009001012999', format: 'code_128' },
|
||||
{ 'name': 'image-006.jpg', 'result': '419055603900009001012999', format: 'code_128' },
|
||||
// GS1-128 barcode with FNC1 characters as field separators
|
||||
{ 'name': 'image-007.jpg', 'result': `${FNC1}42095747${FNC1}9499907123456123456781`, format: 'code_128' },
|
||||
{ 'name': 'image-008.jpg', 'result': '1020185021797280784055', format: 'code_128' },
|
||||
{ 'name': 'image-009.jpg', 'result': '0001285112001000040801', format: 'code_128' },
|
||||
{ 'name': 'image-010.jpg', 'result': '673023', format: 'code_128' },
|
||||
// TODO: need to implement having different inputStream parameters to be able to
|
||||
// read this one -- it works only with inputStream size set to 1600 presently, but
|
||||
// other samples break at that high a size.
|
||||
// { name: 'image-011.png', result: '33c64780-a9c0-e92a-820c-fae7011c11e2' },
|
||||
];
|
||||
runDecoderTestBothHalfSample('code_128_external', (halfSample) => generateConfig({
|
||||
inputStream: {
|
||||
size: 800,
|
||||
singleChannel: false,
|
||||
},
|
||||
locator: {
|
||||
halfSample,
|
||||
},
|
||||
decoder: {
|
||||
readers: ['external_code_128_reader'],
|
||||
},
|
||||
}), externalCode128TestSet, 'code_128'); // Use code_128 fixture path
|
||||
});
|
||||
});
|
||||
113
quagga2/quagga2-1.12.1/test/integration/helpers.ts
Normal file
113
quagga2/quagga2-1.12.1/test/integration/helpers.ts
Normal file
@@ -0,0 +1,113 @@
|
||||
import Quagga from '../../src/quagga';
|
||||
import { QuaggaJSConfigObject } from '../../type-definitions/quagga';
|
||||
import { expect } from 'chai';
|
||||
import { it as mochaIt } from 'mocha';
|
||||
|
||||
// Export our own 'it' with allowFail support
|
||||
export const it = Object.assign(
|
||||
mochaIt,
|
||||
{
|
||||
allowFail: (title: string, callback: Function) => {
|
||||
mochaIt(title, function() {
|
||||
return Promise.resolve().then(() => {
|
||||
return callback.apply(this, arguments);
|
||||
}).catch((err) => {
|
||||
console.trace('* error during test', title, err);
|
||||
this.skip();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export function runDecoderTest(name: string, config: QuaggaJSConfigObject, testSet: Array<{ name: string, result: string, format: string, allowFailInNode?: boolean, allowFailInBrowser?: boolean }>, halfSampleLabel?: string, fixturePath?: string) {
|
||||
const testLabel = halfSampleLabel ? `${name} (${halfSampleLabel})` : name;
|
||||
const actualFixturePath = fixturePath || name;
|
||||
|
||||
describe(`Decoder ${testLabel}`, () => {
|
||||
testSet.forEach((sample) => {
|
||||
// Use the flags on the test item as the authoritative source
|
||||
const isBrowser = typeof window !== 'undefined';
|
||||
const shouldAllowFail = isBrowser ? sample.allowFailInBrowser : sample.allowFailInNode;
|
||||
const testFn = shouldAllowFail ? it.allowFail : it;
|
||||
testFn(`decodes ${sample.name}`, async function() {
|
||||
this.timeout(20000); // need to set a long timeout because laptops sometimes lag like hell in tests when they go low power
|
||||
const thisConfig = {
|
||||
...config,
|
||||
src: `${isBrowser ? '/' : ''}test/fixtures/${actualFixturePath}/${sample.name}`,
|
||||
};
|
||||
const result = await Quagga.decodeSingle(thisConfig);
|
||||
|
||||
// // console.warn(`* Expect result ${JSON.stringify(result)} to be an object`);
|
||||
expect(result).to.be.an('Object');
|
||||
expect(result.codeResult).to.be.an('Object');
|
||||
expect(result.codeResult.code).to.equal(sample.result);
|
||||
expect(result.codeResult.format).to.equal(sample.format);
|
||||
expect(Quagga.canvas).to.be.an('Object');
|
||||
expect(Quagga.canvas.dom).to.be.an('Object');
|
||||
expect(Quagga.canvas.ctx).to.be.an('Object');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Helper function to run decoder tests with both halfSample configurations
|
||||
export function runDecoderTestBothHalfSample(
|
||||
name: string,
|
||||
configGenerator: (halfSample: boolean) => QuaggaJSConfigObject,
|
||||
testSet: Array<{ name: string, result: string, format: string, allowFailInNode?: boolean, allowFailInBrowser?: boolean }>,
|
||||
fixturePath?: string
|
||||
) {
|
||||
describe(`Decoder ${name} (both halfSample configurations)`, () => {
|
||||
runDecoderTest(name, configGenerator(true), testSet, 'halfSample: true', fixturePath);
|
||||
runDecoderTest(name, configGenerator(false), testSet, 'halfSample: false', fixturePath);
|
||||
});
|
||||
}
|
||||
|
||||
// run test that should not fail but no barcode is in the images
|
||||
export function runNoCodeTest(name: string, config: QuaggaJSConfigObject, testSet: Array<{ name: string, result: string, format: string }>, fixturePath?: string) {
|
||||
const actualFixturePath = fixturePath || name;
|
||||
describe(`Not decoding ${name}`, () => {
|
||||
testSet.forEach((sample) => {
|
||||
it(`should run without error (${sample.name})`, async function() {
|
||||
this.timeout(20000); // need to set a long timeout because laptops sometimes lag like hell in tests when they go low power
|
||||
const thisConfig = {
|
||||
...config,
|
||||
src: `${typeof window !== 'undefined' ? '/' : ''}test/fixtures/${actualFixturePath}/${sample.name}`,
|
||||
};
|
||||
const result = await Quagga.decodeSingle(thisConfig);
|
||||
expect(result).to.be.an('Object');
|
||||
// When multiple: false and no decode found, result should have codeResult.code as null or undefined
|
||||
if (result.codeResult) {
|
||||
expect(result.codeResult.code).to.be.null;
|
||||
}
|
||||
// // console.warn(`* Expect result ${JSON.stringify(result)} to be an object`);
|
||||
expect(Quagga.canvas).to.be.an('Object');
|
||||
expect(Quagga.canvas.dom).to.be.an('Object');
|
||||
expect(Quagga.canvas.ctx).to.be.an('Object');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function generateConfig(configOverride: QuaggaJSConfigObject = {}) {
|
||||
const config: QuaggaJSConfigObject = {
|
||||
inputStream: {
|
||||
size: 640,
|
||||
...configOverride.inputStream,
|
||||
},
|
||||
locator: {
|
||||
patchSize: 'medium',
|
||||
halfSample: true,
|
||||
...configOverride.locator,
|
||||
},
|
||||
numOfWorkers: 0,
|
||||
decoder: {
|
||||
readers: ['ean_reader'],
|
||||
...configOverride.decoder,
|
||||
},
|
||||
locate: configOverride.locate,
|
||||
src: null,
|
||||
};
|
||||
return config;
|
||||
}
|
||||
74
quagga2/quagga2-1.12.1/test/integration/integration.spec.ts
Normal file
74
quagga2/quagga2-1.12.1/test/integration/integration.spec.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
// TODO: write a test that ensures that Quagga.decodeSingle returns a Promise when it should
|
||||
// TODO: write a test that tests the multiple: true decoding option, allowing for multiple barcodes in
|
||||
// a single image to be returned.
|
||||
// TODO: write a test that allows for locate: false and locator configs to be tested.
|
||||
|
||||
import Quagga from '../../src/quagga';
|
||||
import { expect } from 'chai';
|
||||
import { runNoCodeTest, generateConfig } from './helpers';
|
||||
|
||||
|
||||
|
||||
describe('Parallel decoding works', () => {
|
||||
it('decodeSingle running in parallel', async () => {
|
||||
// TODO: we should throw in some other formats here too.
|
||||
const testSet = [
|
||||
{ 'name': 'image-001.jpg', 'result': '3574660239843', format: 'ean_13' },
|
||||
{ 'name': 'image-002.jpg', 'result': '8032754490297', format: 'ean_13' },
|
||||
{ 'name': 'image-004.jpg', 'result': '9002233139084', format: 'ean_13' },
|
||||
{ 'name': 'image-003.jpg', 'result': '4006209700068', format: 'ean_13' },
|
||||
{ 'name': 'image-005.jpg', 'result': '8004030044005', format: 'ean_13' },
|
||||
{ 'name': 'image-006.jpg', 'result': '4003626011159', format: 'ean_13' },
|
||||
{ 'name': 'image-007.jpg', 'result': '2111220009686', format: 'ean_13' },
|
||||
{ 'name': 'image-008.jpg', 'result': '9000275609022', format: 'ean_13' },
|
||||
{ 'name': 'image-009.jpg', 'result': '9004593978587', format: 'ean_13' },
|
||||
{ 'name': 'image-010.jpg', 'result': '9002244845578', format: 'ean_13' },
|
||||
];
|
||||
const promises: Array<Promise<any>> = [];
|
||||
|
||||
testSet.forEach(sample => {
|
||||
const config = generateConfig();
|
||||
config.src = `${typeof window !== 'undefined' ? '/' : ''}test/fixtures/ean/${sample.name}`;
|
||||
promises.push(Quagga.decodeSingle(config));
|
||||
});
|
||||
const results = await Promise.all(promises).catch((err) => { console.warn('* error decoding simultaneously', err); throw(err); });
|
||||
const testResults = testSet.map(x => x.result);
|
||||
results.forEach((r, index) => {
|
||||
expect(r).to.be.an('object');
|
||||
expect(r.codeResult).to.be.an('object');
|
||||
expect(r.codeResult.code).to.equal(testResults[index]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Canvas Update Test, avoid DOMException', () => {
|
||||
// This test ensures that Quagga handles edge cases with invalid canvas dimensions
|
||||
// (NaN width/height) without throwing a DOMException during canvas operations.
|
||||
// This is a regression test - the library should gracefully handle invalid dimensions
|
||||
// and return an empty result rather than crashing.
|
||||
describe('works', () => {
|
||||
runNoCodeTest(
|
||||
'no_code',
|
||||
generateConfig({
|
||||
decoder: {
|
||||
readers: ['code_128_reader', 'ean_reader'],
|
||||
},
|
||||
inputStream: {
|
||||
constraints: {
|
||||
width: NaN,
|
||||
height: NaN
|
||||
},
|
||||
singleChannel: false,
|
||||
},
|
||||
locate: false,
|
||||
locator: {
|
||||
halfSample: true,
|
||||
patchSize: 'x-large'
|
||||
}
|
||||
}),
|
||||
[
|
||||
{ 'name': 'image-001.jpg', 'result': null, format: 'code_128' },
|
||||
]
|
||||
);
|
||||
});
|
||||
});
|
||||
90
quagga2/quagga2-1.12.1/test/integration/reader-order.spec.ts
Normal file
90
quagga2/quagga2-1.12.1/test/integration/reader-order.spec.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* Tests to verify that barcode readers are processed in the order specified
|
||||
* in the configuration. This is important for predictable decoding behavior.
|
||||
*
|
||||
* Key findings about reader order:
|
||||
* 1. Internal readers (code_128_reader, ean_reader, etc.) are processed in the
|
||||
* order they appear in the `readers` config array
|
||||
* 2. External readers must be registered via `Quagga.registerReader()` BEFORE
|
||||
* they can be used in the `readers` array
|
||||
* 3. The position of a reader in the `readers` array determines when it attempts
|
||||
* to decode (earlier = higher priority)
|
||||
* 4. The first reader to successfully decode the barcode wins
|
||||
*/
|
||||
|
||||
import Quagga from '../../src/quagga';
|
||||
import { expect } from 'chai';
|
||||
|
||||
/**
|
||||
* Helper function to construct fixture paths consistently across browser and Node environments
|
||||
*/
|
||||
function getFixturePath(folder: string, filename: string): string {
|
||||
const prefix = typeof window !== 'undefined' ? '/' : '';
|
||||
return `${prefix}test/fixtures/${folder}/${filename}`;
|
||||
}
|
||||
|
||||
describe('Priority Behavior with Multiple Readers', () => {
|
||||
it('should decode EAN-8 as ean_8 when ean_8_reader is prioritized over ean_reader', async function() {
|
||||
this.timeout(20000);
|
||||
|
||||
// This test uses an EAN-8 barcode image (8 digits)
|
||||
// When ean_8_reader is listed first, it should decode as ean_8
|
||||
const config = {
|
||||
inputStream: {
|
||||
size: 640,
|
||||
},
|
||||
locator: {
|
||||
patchSize: 'large',
|
||||
halfSample: true,
|
||||
},
|
||||
numOfWorkers: 0,
|
||||
decoder: {
|
||||
// EAN-8 reader first - should decode EAN-8 barcodes as ean_8
|
||||
readers: ['ean_8_reader', 'ean_reader'],
|
||||
},
|
||||
locate: true,
|
||||
src: getFixturePath('ean_8', 'image-001.jpg'),
|
||||
};
|
||||
|
||||
const result = await Quagga.decodeSingle(config);
|
||||
|
||||
expect(result).to.be.an('object');
|
||||
expect(result.codeResult).to.be.an('object');
|
||||
expect(result.codeResult.code).to.equal('42191605');
|
||||
expect(result.codeResult.format).to.equal('ean_8');
|
||||
});
|
||||
|
||||
it('should fallback to ean_8_reader when ean_reader cannot decode EAN-8 barcode', async function() {
|
||||
this.timeout(20000);
|
||||
|
||||
// EAN-8 and EAN-13 are different barcode formats with different structures.
|
||||
// The EAN-13 reader will not successfully decode an EAN-8 barcode,
|
||||
// so it will return null and the decoder will try the next reader.
|
||||
// This test demonstrates that reader order affects fallback behavior.
|
||||
const config = {
|
||||
inputStream: {
|
||||
size: 640,
|
||||
},
|
||||
locator: {
|
||||
patchSize: 'large',
|
||||
halfSample: true,
|
||||
},
|
||||
numOfWorkers: 0,
|
||||
decoder: {
|
||||
// EAN-13 reader first, EAN-8 as fallback
|
||||
readers: ['ean_reader', 'ean_8_reader'],
|
||||
},
|
||||
locate: true,
|
||||
src: getFixturePath('ean_8', 'image-001.jpg'),
|
||||
};
|
||||
|
||||
const result = await Quagga.decodeSingle(config);
|
||||
|
||||
// EAN-13 reader cannot decode EAN-8, so EAN-8 reader succeeds as fallback
|
||||
expect(result).to.be.an('object');
|
||||
expect(result.codeResult).to.be.an('object');
|
||||
expect(result.codeResult.code).to.equal('42191605');
|
||||
// Since EAN-13 reader fails, the EAN-8 reader handles it
|
||||
expect(result.codeResult.format).to.equal('ean_8');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user