Skip to content
Merged
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,7 @@ jobs:
- name: install Node.js
uses: actions/setup-node@master
with:
node-version: 13.x
node-version: 14.x

- name: install dependencies
run: npm ci
Expand Down
80 changes: 80 additions & 0 deletions src/models/Transcription.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
/**
* @module models.Transcription
*/

import isAbbreviation from '../utilities/types/isAbbreviation.js';

/**
* Validates an abbreviation. Throws a type error if the input is not a valid abbreviation.
* @param {Any} input The input to validate
*/
function validateAbbreviation(input) {
if (!isAbbreviation(input)) {
const e = new TypeError(`Each orthography key must be a valid abbreviation.`);
e.name = `TranscriptionOrthoError`;
throw e;
}
}

/**
* Validates a String for Transcription values. Throws a type error if the input is not a String.
* @param {Any} input The input to validate
*/
function validateString(input) {
if (typeof input !== `string`) {
const e = new TypeError(`Each piece of data in a Transcription must be a String of text in a particular orthography.`);
e.name = `TranscriptionStringError`;
throw e;
}
}

/**
* A class representing a Transcription, as a JavaScript Map Object. See the [DLx Data Format]{@link https://format.digitallinguistics.io/schemas/Transcription.html} for information about formatting Transcription objects.
* @memberof models
* @extends Map
*
* @example
* const transcription = new Transcription({
* latin: `hello`,
* IPA: `hɛˈloʊ`,
* });
*
* console.log(transcription.get(`ipa`)); // hɛˈloʊ
*/
class Transcription extends Map {

/**
* Create a new Transcription
* @param {Map|Object} [data={}] The data to use for this Transcription, as either a Map or an Object.
*/
constructor(data = {}) {

if (typeof data !== `object`) {
const e = new TypeError(`The data passed to the Transcription class must be a Map or Object.`);
e.name = `TranscriptionDataError`;
throw e;
}

// eslint-disable-next-line no-param-reassign
data = data instanceof Map ? Object.fromEntries(data) : data;

Object.keys(data).forEach(validateAbbreviation);
Object.values(data).forEach(validateString);

super(Object.entries(data));

}

set(key, val) {
validateAbbreviation(key);
validateString(val);
return super.set(key, val);
}

toJSON() {
return Object.fromEntries(this);
}

}

export default Transcription;
78 changes: 78 additions & 0 deletions src/models/Transcription.test.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
/**
* @module models.MultiLangString
*/

import Transcription from './Transcription.js';

describe(`Transcription`, () => {

const testData = {
IPA: `hɛˈloʊ`,
Latin: `hello`,
};

it(`is an empty Map when no data is provided`, () => {

const txn = new Transcription;

txn.should.be.instanceOf(Map);
txn.size.should.equal(0);

});

it(`maps orthographies to transcriptions`, () => {

const txn = new Transcription(testData);

txn.get(`IPA`).should.equal(`hɛˈloʊ`);
txn.get(`Latin`).should.equal(`hello`);

});

it(`only allows abbreviations as keys`, () => {

() => new Transcription({ 'bad key': 'hello' })
.should.throw()
.with.property(`name`, `TranscriptionOrthoError`);

});

it(`only allows strings as values`, () => {

() => new Transcription({ eng: 0 })
.should.throw()
.with.property(`name`, `TranscriptionStringError`);

});

it(`validates new keys`, () => {

const txn = new Transcription;

() => txn.set(`bad key`, `hello`)
.should.throw()
.with.property(`name`, `TranscriptionOrthoError`);

});

it(`validates new values`, () => {

const txn = new Transcription;

() => txn.set(`eng`, undefined)
.should.throw()
.with.property(`name`, `TranscriptionStringError`);

});

it(`stringifies as an Object`, () => {

const txn = new Transcription(testData);
const pojo = JSON.parse(JSON.stringify(txn));

pojo.IPA.should.equal(testData.IPA);
pojo.Latin.should.equal(testData.Latin);

});

});
53 changes: 53 additions & 0 deletions src/models/Utterance.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
import Model from '../core/Model.js';
import MultiLangString from './MultiLangString.js';
import Transcription from './Transcription.js';

/**
* A class representing an utterance.
* @memberof models
* @extends core.Model
*/
class Utterance extends Model {

/**
* The transcript of this utterance, as a Transcription object
* @type {Transcription}
*/
#transcript;

/**
* The transcription of this utterance, as a Transcription object
* @type {Transcription}
*/
#transcription;

/**
* The translation of this utterance, as a MultiLangString object
* @type {MultiLangString}
*/
#translation;

/**
* Create a new Utterance
* @param {Object} [data={}] The data to use for this Utterance
*/
constructor(data = {}) {

super();

Model.defineModelProp(this, `transcript`, Transcription);
Model.defineModelProp(this, `transcription`, Transcription);
Model.defineModelProp(this, `translation`, MultiLangString);

Object.assign(this, data);

// Required properties
// TODO: replace with: this.transcription ??= new Transcription;
this.transcription = this.transcription ?? new Transcription;
this.translation = this.translation ?? new MultiLangString;

}

}

export default Utterance;
63 changes: 63 additions & 0 deletions src/models/Utterance.test.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
import chai from 'chai';
import MultiLangString from './MultiLangString.js';
import Transcription from './Transcription.js';
import Utterance from './Utterance.js';

const should = chai.should();

describe(`Utterance`, () => {

const testData = { eng: 'Hello world!' };

it(`instantiates without data`, () => {
(() => new Utterance).should.not.throw();
});

it(`transcript is a Transcription object`, () => {

const utterance = new Utterance({ transcript: testData });

utterance.transcript.should.be.instanceOf(Transcription);
utterance.transcript.get(`eng`).should.equal(testData.eng);

});

it(`transcript is undefined if absent`, () => {
const utterance = new Utterance;
should.not.exist(utterance.transcript);
});

it(`transcription is a Transcription object`, () => {
const utterance = new Utterance({ transcription: testData });
utterance.transcription.should.be.instanceOf(Transcription);
utterance.transcription.get(`eng`).should.equal(testData.eng);
});

it(`transcription is an empty Transcription (Map) object if absent`, () => {

const utterance = new Utterance;

utterance.transcription.should.be.instanceOf(Transcription);
utterance.transcription.size.should.equal(0);

});

it(`translation is a MultiLangString object`, () => {

const utterance = new Utterance({ translation: testData });

utterance.translation.should.be.instanceOf(MultiLangString);
utterance.translation.get(`eng`).should.equal(testData.eng);

});

it(`translation is an empty MultiLangString if absent`, () => {

const utterance = new Utterance;

utterance.translation.should.be.instanceOf(MultiLangString);
utterance.translation.size.should.equal(0);

});

});
2 changes: 2 additions & 0 deletions src/models/index.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,3 +5,5 @@

export { default as Language } from './Language.js';
export { default as MultiLangString } from './MultiLangString.js';
export { default as Transcription } from './Transcription.js';
export { default as Utterance } from './Utterance.js';
12 changes: 10 additions & 2 deletions src/models/index.test.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,8 +2,14 @@
max-nested-callbacks,
Comment thread
dwhieb marked this conversation as resolved.
*/

import chai from 'chai';
import { Language, MultiLangString } from './index.js';
import chai from 'chai';

import {
Language,
MultiLangString,
Transcription,
Utterance,
} from './index.js';

chai.should();

Expand All@@ -12,6 +18,8 @@ describe(`models`, () => {
it(`has the expected exports`, () => {
MultiLangString.name.should.equal(`MultiLangString`);
Language.name.should.equal(`Language`);
Transcription.name.should.equal(`Transcription`);
Utterance.name.should.equal(`Utterance`);
});

});