@votingworks/ballot-encoder
Advanced tools
Comparing version 1.1.0 to 1.2.0
{ | ||
"name": "@votingworks/ballot-encoder", | ||
"version": "1.1.0", | ||
"version": "1.2.0", | ||
"files": [ | ||
@@ -5,0 +5,0 @@ "src/**/*.{js,d.ts,d.ts.map,json}" |
@@ -105,2 +105,3 @@ "use strict"; | ||
} | ||
// eslint-disable-next-line class-methods-use-this | ||
sizeofUint({ max, size }) { | ||
@@ -107,0 +108,0 @@ if (typeof max !== 'undefined' && typeof size !== 'undefined') { |
@@ -64,2 +64,13 @@ import { Uint8, Uint1 } from './types'; | ||
/** | ||
* Calls back with this `BitWriter` to make chaining easier. | ||
* | ||
* @example | ||
* | ||
* return new BitWriter() | ||
* .writeBoolean(true) | ||
* .with(writer => encodeSomething(writer)) | ||
* .writeBoolean(false) | ||
*/ | ||
with(callback: (writer: this) => void): this; | ||
/** | ||
* Converts the data written to this `BitWriter` to a `Uint8Array`. | ||
@@ -66,0 +77,0 @@ */ |
@@ -113,2 +113,16 @@ "use strict"; | ||
/** | ||
* Calls back with this `BitWriter` to make chaining easier. | ||
* | ||
* @example | ||
* | ||
* return new BitWriter() | ||
* .writeBoolean(true) | ||
* .with(writer => encodeSomething(writer)) | ||
* .writeBoolean(false) | ||
*/ | ||
with(callback) { | ||
callback(this); | ||
return this; | ||
} | ||
/** | ||
* Converts the data written to this `BitWriter` to a `Uint8Array`. | ||
@@ -115,0 +129,0 @@ */ |
@@ -92,2 +92,3 @@ "use strict"; | ||
.debug(); | ||
/* eslint-disable no-console */ | ||
expect(console.log).toHaveBeenNthCalledWith(1, 'wrote true'); | ||
@@ -98,2 +99,3 @@ expect(console.log).toHaveBeenNthCalledWith(2, '1'); | ||
expect(console.log).toHaveBeenNthCalledWith(5, '10000000 01000000 10000000 11'); | ||
/* eslint-enable no-console */ | ||
}); |
@@ -78,2 +78,8 @@ export declare type VoidFunction = () => void; | ||
export declare type VotesDict = Dictionary<Vote>; | ||
export declare enum BallotType { | ||
Standard = 0, | ||
Absentee = 1, | ||
Provisional = 2 | ||
} | ||
export declare const BallotTypeMaximumValue: number; | ||
export interface CompletedBallot { | ||
@@ -86,2 +92,3 @@ election: Election; | ||
isTestBallot: boolean; | ||
ballotType: BallotType; | ||
} | ||
@@ -162,9 +169,9 @@ export declare type CardDataTypes = 'voter' | 'pollworker' | 'clerk'; | ||
* // Vote by candidate id. | ||
* votes(contests, { president: 'boone-lian' }) | ||
* vote(contests, { president: 'boone-lian' }) | ||
* | ||
* // Vote by yesno contest. | ||
* votes(contests, { 'question-a': 'yes' }) | ||
* vote(contests, { 'question-a': 'yes' }) | ||
* | ||
* // Multiple votes. | ||
* votes(contests, { | ||
* vote(contests, { | ||
* president: 'boone-lian', | ||
@@ -175,3 +182,3 @@ * 'question-a': 'yes' | ||
* // Multiple candidate selections. | ||
* votes(contests, { | ||
* vote(contests, { | ||
* 'city-council': ['rupp', 'davis'] | ||
@@ -178,0 +185,0 @@ * }) |
@@ -7,2 +7,11 @@ "use strict"; | ||
const electionSample_json_1 = __importDefault(require("./data/electionSample.json")); | ||
var BallotType; | ||
(function (BallotType) { | ||
BallotType[BallotType["Standard"] = 0] = "Standard"; | ||
BallotType[BallotType["Absentee"] = 1] = "Absentee"; | ||
BallotType[BallotType["Provisional"] = 2] = "Provisional"; | ||
})(BallotType = exports.BallotType || (exports.BallotType = {})); | ||
// Updating this value is a breaking change. | ||
// eslint-disable-next-line no-bitwise | ||
exports.BallotTypeMaximumValue = (1 << 4) - 1; | ||
/** | ||
@@ -58,9 +67,9 @@ * Gets contests which belong to a ballot style in an election. | ||
* // Vote by candidate id. | ||
* votes(contests, { president: 'boone-lian' }) | ||
* vote(contests, { president: 'boone-lian' }) | ||
* | ||
* // Vote by yesno contest. | ||
* votes(contests, { 'question-a': 'yes' }) | ||
* vote(contests, { 'question-a': 'yes' }) | ||
* | ||
* // Multiple votes. | ||
* votes(contests, { | ||
* vote(contests, { | ||
* president: 'boone-lian', | ||
@@ -71,3 +80,3 @@ * 'question-a': 'yes' | ||
* // Multiple candidate selections. | ||
* votes(contests, { | ||
* vote(contests, { | ||
* 'city-council': ['rupp', 'davis'] | ||
@@ -74,0 +83,0 @@ * }) |
@@ -37,3 +37,3 @@ "use strict"; | ||
const president = contests[0]; | ||
const candidates = president.candidates; | ||
const { candidates } = president; | ||
expect(election_1.vote(contests, { president: candidates })).toEqual({ | ||
@@ -40,0 +40,0 @@ president: candidates, |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const _1 = require("."); | ||
const election_1 = require("./election"); | ||
const _1 = require("."); | ||
test('exports v0 encoding', () => { | ||
@@ -28,2 +28,3 @@ expect(typeof _1.v0.encodeBallot).toBe('function'); | ||
isTestBallot: false, | ||
ballotType: election_1.BallotType.Standard, | ||
}; | ||
@@ -45,2 +46,3 @@ expect(_1.detect(_1.encodeBallot(ballot))).toEqual(_1.EncoderVersion.v1); | ||
isTestBallot: false, | ||
ballotType: election_1.BallotType.Standard, | ||
}; | ||
@@ -66,2 +68,3 @@ expect(_1.encodeBallot(ballot, _1.EncoderVersion.v0)).toEqual(_1.v0.encodeBallot(ballot)); | ||
isTestBallot: false, | ||
ballotType: election_1.BallotType.Standard, | ||
}; | ||
@@ -86,2 +89,3 @@ expect(_1.decodeBallot(election_1.electionSample, _1.v0.encodeBallot(ballot), _1.EncoderVersion.v0)).toEqual({ | ||
isTestBallot: false, | ||
ballotType: election_1.BallotType.Standard, | ||
}; | ||
@@ -103,2 +107,3 @@ expect(_1.decodeBallot(election_1.electionSample, _1.v1.encodeBallot(ballot), _1.EncoderVersion.v1)).toEqual({ version: _1.EncoderVersion.v1, ballot }); | ||
isTestBallot: false, | ||
ballotType: election_1.BallotType.Standard, | ||
}; | ||
@@ -130,4 +135,5 @@ expect(_1.decodeBallot(election_1.electionSample, _1.v0.encodeBallot(ballot))).toEqual({ | ||
isTestBallot: false, | ||
ballotType: election_1.BallotType.Standard, | ||
}; | ||
expect(_1.detect(_1.v0.encodeBallot(ballot))).toEqual(_1.EncoderVersion.v0); | ||
}); |
/** | ||
* v0 ballot encoding format. See README.md in this directory for more information. | ||
*/ | ||
import { Election, CompletedBallot } from '../election'; | ||
import { CompletedBallot, Election } from '../election'; | ||
export declare const MAXIMUM_WRITE_IN_LENGTH = 40; | ||
@@ -6,0 +6,0 @@ /** |
@@ -117,2 +117,3 @@ "use strict"; | ||
isTestBallot: false, | ||
ballotType: election_1.BallotType.Standard, | ||
}; | ||
@@ -119,0 +120,0 @@ } |
@@ -11,4 +11,4 @@ "use strict"; | ||
const election_1 = require("../election"); | ||
const v1 = __importStar(require("../v1")); | ||
const index_1 = require("./index"); | ||
const v1 = __importStar(require("../v1")); | ||
test('can detect an encoded v0 ballot', () => { | ||
@@ -35,2 +35,3 @@ expect(index_1.detectString('12.23.0|2|3|2|4|1|0|2|5,3,6,0|W||2,0,W|1|1|0|1|1|0|1|1.Ei5PXq7xbSJWHrF1dNRsjg')).toBe(true); | ||
isTestBallot: false, | ||
ballotType: election_1.BallotType.Standard, | ||
}; | ||
@@ -65,2 +66,3 @@ expect(index_1.detect(v1.encodeBallot(ballot))).toBe(false); | ||
isTestBallot: false, | ||
ballotType: election_1.BallotType.Standard, | ||
}; | ||
@@ -82,2 +84,3 @@ expect(index_1.decodeBallot(election_1.electionSample, index_1.encodeBallot(ballot))).toEqual(ballot); | ||
isTestBallot: false, | ||
ballotType: election_1.BallotType.Standard, | ||
}; | ||
@@ -105,2 +108,3 @@ const encodedBallot = '12.23.|||||||||||||||||||.abcde'; | ||
isTestBallot: false, | ||
ballotType: election_1.BallotType.Standard, | ||
}; | ||
@@ -127,2 +131,3 @@ const encodedBallot = '12.23.||||||||||||1|0||||||.abcde'; | ||
isTestBallot: false, | ||
ballotType: election_1.BallotType.Standard, | ||
}; | ||
@@ -151,2 +156,3 @@ const encodedBallot = '12.23.0,1|||||||||||||||||||.abcde'; | ||
isTestBallot: false, | ||
ballotType: election_1.BallotType.Standard, | ||
}; | ||
@@ -165,3 +171,5 @@ const encodedBallot = '12.23.W|||||||||||||||||||.abcde'; | ||
// v0 does not encode whether a ballot is a test ballot | ||
isTestBallot: false })); | ||
isTestBallot: false, | ||
// v0 does not encode ballot type | ||
ballotType: election_1.BallotType.Standard })); | ||
}); | ||
@@ -184,2 +192,3 @@ test('cannot encode a yesno contest with an invalid value', () => { | ||
isTestBallot: false, | ||
ballotType: election_1.BallotType.Standard, | ||
}; | ||
@@ -186,0 +195,0 @@ expect(() => index_1.encodeBallot(ballot)).toThrowError('cannot encode yesno vote, expected "no" or "yes" but got "YEP"'); |
@@ -0,3 +1,3 @@ | ||
import { BitReader, BitWriter, CustomEncoding, Uint8 } from '../bits'; | ||
import { CompletedBallot, Election } from '../election'; | ||
import { BitReader, BitWriter, CustomEncoding, Uint8 } from '../bits'; | ||
export declare const MAXIMUM_WRITE_IN_LENGTH = 40; | ||
@@ -11,5 +11,5 @@ export declare const WriteInEncoding: CustomEncoding; | ||
export declare function encodeBallot(ballot: CompletedBallot): Uint8Array; | ||
export declare function encodeBallotInto({ election, ballotStyle, precinct, votes, ballotId, isTestBallot, }: CompletedBallot, bits: BitWriter): BitWriter; | ||
export declare function encodeBallotInto({ election, ballotStyle, precinct, votes, ballotId, isTestBallot, ballotType, }: CompletedBallot, bits: BitWriter): BitWriter; | ||
export declare function decodeBallot(election: Election, data: Uint8Array): CompletedBallot; | ||
export declare function decodeBallotFromReader(election: Election, bits: BitReader): CompletedBallot; | ||
//# sourceMappingURL=index.d.ts.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const bits_1 = require("../bits"); | ||
const election_1 = require("../election"); | ||
const bits_1 = require("../bits"); | ||
exports.MAXIMUM_WRITE_IN_LENGTH = 40; | ||
@@ -28,11 +28,13 @@ // TODO: include "magic number" and encoding version | ||
exports.encodeBallot = encodeBallot; | ||
function encodeBallotInto({ election, ballotStyle, precinct, votes, ballotId, isTestBallot, }, bits) { | ||
function encodeBallotInto({ election, ballotStyle, precinct, votes, ballotId, isTestBallot, ballotType, }, bits) { | ||
election_1.validateVotes({ election, ballotStyle, votes }); | ||
const contests = election_1.getContests({ ballotStyle, election }); | ||
bits | ||
return bits | ||
.writeUint8(...exports.Prelude) | ||
.writeString(ballotStyle.id) | ||
.writeString(precinct.id) | ||
.writeString(ballotId); | ||
return encodeBallotVotesInto(contests, votes, bits).writeBoolean(isTestBallot); | ||
.writeString(ballotId) | ||
.with(() => encodeBallotVotesInto(contests, votes, bits)) | ||
.writeBoolean(isTestBallot) | ||
.writeUint(ballotType, { max: election_1.BallotTypeMaximumValue }); | ||
} | ||
@@ -108,2 +110,3 @@ exports.encodeBallotInto = encodeBallotInto; | ||
const isTestBallot = bits.readBoolean(); | ||
const ballotType = bits.readUint({ max: election_1.BallotTypeMaximumValue }); | ||
readPaddingToEnd(bits); | ||
@@ -117,2 +120,3 @@ return { | ||
isTestBallot, | ||
ballotType, | ||
}; | ||
@@ -119,0 +123,0 @@ } |
@@ -36,2 +36,3 @@ "use strict"; | ||
isTestBallot: false, | ||
ballotType: election_1.BallotType.Standard, | ||
}; | ||
@@ -53,2 +54,3 @@ expect(index_1.detect(v0.encodeBallot(ballot))).toBe(false); | ||
isTestBallot: false, | ||
ballotType: election_1.BallotType.Standard, | ||
}; | ||
@@ -70,2 +72,3 @@ expect(index_1.decodeBallot(election_1.electionSample, index_1.encodeBallot(ballot))).toEqual(ballot); | ||
isTestBallot: false, | ||
ballotType: election_1.BallotType.Standard, | ||
}; | ||
@@ -84,2 +87,6 @@ const encodedBallot = new bits_1.BitWriter() | ||
.writeBoolean(...contests.map(() => false)) | ||
// test ballot? | ||
.writeBoolean(false) | ||
// ballot type | ||
.writeUint(election_1.BallotType.Standard, { max: election_1.BallotTypeMaximumValue }) | ||
.toUint8Array(); | ||
@@ -102,2 +109,3 @@ expect(index_1.encodeBallot(ballot)).toEqualBits(encodedBallot); | ||
isTestBallot: true, | ||
ballotType: election_1.BallotType.Standard, | ||
}; | ||
@@ -118,2 +126,4 @@ const encodedBallot = new bits_1.BitWriter() | ||
.writeBoolean(true) | ||
// ballot type | ||
.writeUint(election_1.BallotType.Standard, { max: election_1.BallotTypeMaximumValue }) | ||
.toUint8Array(); | ||
@@ -123,2 +133,37 @@ expect(index_1.encodeBallot(ballot)).toEqualBits(encodedBallot); | ||
}); | ||
it('encodes & decodes the ballot type', () => { | ||
const ballotStyle = election_1.electionSample.ballotStyles[0]; | ||
const precinct = election_1.electionSample.precincts[0]; | ||
const contests = election_1.getContests({ ballotStyle, election: election_1.electionSample }); | ||
const votes = election_1.vote(contests, {}); | ||
const ballotId = 'abcde'; | ||
const ballot = { | ||
election: election_1.electionSample, | ||
ballotId, | ||
ballotStyle, | ||
precinct, | ||
votes, | ||
isTestBallot: true, | ||
ballotType: election_1.BallotType.Absentee, | ||
}; | ||
const encodedBallot = new bits_1.BitWriter() | ||
// prelude + version number | ||
.writeString('VX', { includeLength: false }) | ||
.writeUint8(1) | ||
// ballot style id | ||
.writeString('12') | ||
// precinct id | ||
.writeString('23') | ||
// ballot Id | ||
.writeString('abcde') | ||
// vote roll call only, no vote data | ||
.writeBoolean(...contests.map(() => false)) | ||
// test ballot? | ||
.writeBoolean(true) | ||
// ballot type | ||
.writeUint(election_1.BallotType.Absentee, { max: election_1.BallotTypeMaximumValue }) | ||
.toUint8Array(); | ||
expect(index_1.encodeBallot(ballot)).toEqualBits(encodedBallot); | ||
expect(index_1.decodeBallot(election_1.electionSample, encodedBallot)).toEqual(ballot); | ||
}); | ||
it('encodes & decodes yesno votes correctly', () => { | ||
@@ -146,2 +191,3 @@ const ballotStyle = election_1.electionSample.ballotStyles[0]; | ||
isTestBallot: false, | ||
ballotType: election_1.BallotType.Standard, | ||
}; | ||
@@ -169,2 +215,6 @@ const encodedBallot = new bits_1.BitWriter() | ||
.writeBoolean(true) | ||
// test ballot? | ||
.writeBoolean(false) | ||
// ballot type | ||
.writeUint(election_1.BallotType.Standard, { max: election_1.BallotTypeMaximumValue }) | ||
.toUint8Array(); | ||
@@ -200,2 +250,3 @@ expect(index_1.encodeBallot(ballot)).toEqualBits(encodedBallot); | ||
isTestBallot: false, | ||
ballotType: election_1.BallotType.Standard, | ||
}; | ||
@@ -243,2 +294,6 @@ const encodedBallot = new bits_1.BitWriter() | ||
.writeUint(0, { max: 2 }) // 3 seats - 1 selection = 2 write-ins max | ||
// test ballot? | ||
.writeBoolean(false) | ||
// ballot type | ||
.writeUint(election_1.BallotType.Standard, { max: election_1.BallotTypeMaximumValue }) | ||
.toUint8Array(); | ||
@@ -265,2 +320,3 @@ expect(index_1.encodeBallot(ballot)).toEqualBits(encodedBallot); | ||
isTestBallot: false, | ||
ballotType: election_1.BallotType.Standard, | ||
}; | ||
@@ -290,2 +346,4 @@ const encodedBallot = new bits_1.BitWriter() | ||
.writeBoolean(false) | ||
// ballot type | ||
.writeUint(election_1.BallotType.Standard, { max: election_1.BallotTypeMaximumValue }) | ||
.toUint8Array(); | ||
@@ -344,2 +402,3 @@ expect(index_1.encodeBallot(ballot)).toEqualBits(encodedBallot); | ||
isTestBallot: false, | ||
ballotType: election_1.BallotType.Standard, | ||
}; | ||
@@ -364,2 +423,3 @@ const writer = new bits_1.BitWriter(); | ||
isTestBallot: false, | ||
ballotType: election_1.BallotType.Standard, | ||
}; | ||
@@ -366,0 +426,0 @@ const writer = new bits_1.BitWriter(); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
234355
3654