test/taxonomic-units.js
/*
* Test taxonomic unit construction and matching.
*/
const chai = require('chai');
const phyx = require('../src');
// Use Chai's expect API.
const expect = chai.expect;
/*
* We primarily test two classes here:
* - TaxonomicUnitWrapper, which wraps a taxonomic unit and determines if it
* refers to a scientific name, specimen identifier or external reference,
* or a combination of these.
* - TaxonomicUnitMatcher, which accepts two taxonomic units and determines
* whether and for what reason the two can be matched.
*/
describe('TaxonomicUnitWrapper', function () {
describe('#constructor given no arguments', function () {
it('should create an empty TaxonomicUnitWrapper without a defined label', function () {
// Empty TU without @type.
let wrapper = new phyx.TaxonomicUnitWrapper({});
expect(wrapper).to.be.instanceOf(phyx.TaxonomicUnitWrapper);
expect(wrapper.label).to.be.undefined;
// Empty TU with type TYPE_TAXON_CONCEPT.
wrapper = new phyx.TaxonomicUnitWrapper({
'@type': phyx.TaxonomicUnitWrapper.TYPE_TAXON_CONCEPT,
});
expect(wrapper).to.be.instanceOf(phyx.TaxonomicUnitWrapper);
expect(wrapper.label).to.be.undefined;
// Empty TU with type TYPE_SPECIMEN.
wrapper = new phyx.TaxonomicUnitWrapper({
'@type': phyx.TaxonomicUnitWrapper.TYPE_SPECIMEN,
});
expect(wrapper).to.be.instanceOf(phyx.TaxonomicUnitWrapper);
expect(wrapper.label).to.be.undefined;
// Empty TU with type TYPE_SPECIMEN and a taxonomic name.
wrapper = new phyx.TaxonomicUnitWrapper({
'@type': phyx.TaxonomicUnitWrapper.TYPE_SPECIMEN,
nameString: 'Taxonomic name',
});
expect(wrapper).to.be.instanceOf(phyx.TaxonomicUnitWrapper);
expect(wrapper.label).to.be.undefined;
});
});
describe('#label given a taxonomic unit', function () {
it('should return a wrapped scientific name', function () {
const wrapper = new phyx.TaxonomicUnitWrapper({
'@type': phyx.TaxonomicUnitWrapper.TYPE_TAXON_CONCEPT,
hasName: {
label: 'Ornithorhynchus anatinus (Shaw, 1799)',
nameComplete: 'Ornithorhynchus anatinus',
},
});
expect(wrapper.label).to.equal('Ornithorhynchus anatinus (Shaw, 1799)');
});
it('should return a wrapped specimen identifier preceded by "Specimen"', function () {
const wrapper = new phyx.TaxonomicUnitWrapper({
'@type': phyx.TaxonomicUnitWrapper.TYPE_SPECIMEN,
occurrenceID: 'MVZ 225749',
});
expect(wrapper.label).to.equal('Specimen MVZ 225749');
});
it('should return specimens with an occurrenceID as well as a taxon concept', function () {
const wrapper = new phyx.TaxonomicUnitWrapper({
'@type': [
phyx.TaxonomicUnitWrapper.TYPE_SPECIMEN,
],
nameString: 'Rana luteiventris',
occurrenceID: 'MVZ 225749',
});
expect(wrapper.label).to.equal('Specimen MVZ 225749 identified as Rana luteiventris');
});
it('should ignore occurrence ID if typed as a taxon concept', function () {
const wrapper = new phyx.TaxonomicUnitWrapper({
'@type': phyx.TaxonomicUnitWrapper.TYPE_TAXON_CONCEPT,
nameString: 'Rana luteiventris',
occurrenceID: 'MVZ 225749',
});
expect(wrapper.label).to.equal('Rana luteiventris');
});
it('should return a wrapped external reference by surrounding it with "<>"', function () {
const wrapper = new phyx.TaxonomicUnitWrapper({
'@id': [
'http://arctos.database.museum/guid/MVZ:Herp:225749',
],
});
expect(wrapper.label).to.equal('<http://arctos.database.museum/guid/MVZ:Herp:225749>');
});
it('should provide both taxon name and occurrence ID in label, but ignore external reference', function () {
const wrapper = new phyx.TaxonomicUnitWrapper({
'@id': [
'http://arctos.database.museum/guid/MVZ:Herp:225749',
],
'@type': phyx.TaxonomicUnitWrapper.TYPE_SPECIMEN,
nameString: 'Rana luteiventris',
occurrenceID: 'MVZ 225749',
});
expect(wrapper.label).to.equal('Specimen MVZ 225749 identified as Rana luteiventris');
});
});
describe('#fromLabel', function () {
it('should return empty lists when inputs are empty or undefined', function () {
expect(phyx.TaxonomicUnitWrapper.fromLabel()).to.be.undefined;
expect(phyx.TaxonomicUnitWrapper.fromLabel(undefined)).to.be.undefined;
expect(phyx.TaxonomicUnitWrapper.fromLabel(null)).to.be.undefined;
expect(phyx.TaxonomicUnitWrapper.fromLabel('')).to.be.undefined;
expect(phyx.TaxonomicUnitWrapper.fromLabel(' ')).to.be.undefined;
});
it('when given a scientific name, it should return a list of a single TU wrapping a scientific name', function () {
expect(phyx.TaxonomicUnitWrapper.fromLabel('Rana luteiventris MVZ225749'))
.to.be.deep.equal({
'@type': 'http://rs.tdwg.org/ontology/voc/TaxonConcept#TaxonConcept',
label: 'Rana luteiventris MVZ225749',
hasName: {
'@type': 'http://rs.tdwg.org/ontology/voc/TaxonName#TaxonName',
label: 'Rana luteiventris MVZ225749',
genusPart: 'Rana',
specificEpithet: 'luteiventris',
nameComplete: 'Rana luteiventris',
},
});
});
it('when given a scientific name separated with underscores, it should return a list of a single TU wrapping the scientific name', function () {
expect(phyx.TaxonomicUnitWrapper.fromLabel('Rana_luteiventris_MVZ_225749'))
.to.be.deep.equal({
'@type': 'http://rs.tdwg.org/ontology/voc/TaxonConcept#TaxonConcept',
label: 'Rana_luteiventris_MVZ_225749',
hasName: {
'@type': 'http://rs.tdwg.org/ontology/voc/TaxonName#TaxonName',
label: 'Rana_luteiventris_MVZ_225749',
nameComplete: 'Rana luteiventris',
genusPart: 'Rana',
specificEpithet: 'luteiventris',
},
});
});
});
describe('#asOWLEquivClass', function () {
it('when given a taxon concept, only the complete name should be present in the equivClass', function () {
const wrapper = new phyx.TaxonomicUnitWrapper({
'@type': phyx.TaxonomicUnitWrapper.TYPE_TAXON_CONCEPT,
nameString: 'Rana luteiventris Thompson, 1913',
});
expect(wrapper.asOWLEquivClass).to.deep.equal({
'@type': 'owl:Restriction',
onProperty: 'http://rs.tdwg.org/ontology/voc/TaxonConcept#hasName',
someValuesFrom: {
'@type': 'owl:Restriction',
onProperty: 'http://rs.tdwg.org/ontology/voc/TaxonName#nameComplete',
hasValue: 'Rana luteiventris',
},
});
});
it('when given a specimen, only the occurrence ID should be present in the equivClass', function () {
const wrapper = new phyx.TaxonomicUnitWrapper({
'@type': phyx.TaxonomicUnitWrapper.TYPE_SPECIMEN,
nameString: 'Rana luteiventris',
occurrenceID: 'MVZ 225749',
});
expect(wrapper.asOWLEquivClass).to.deep.equal({
'@type': 'owl:Restriction',
onProperty: 'http://rs.tdwg.org/dwc/terms/occurrenceID',
hasValue: 'MVZ 225749',
});
});
});
});
describe('TaxonomicUnitMatcher', function () {
// To test matching, let's set up some taxonomic units.
// Note that:
// tunit1 and tunit2 should match by scientific name.
// tunit2 and tunit3 should match by specimen identifier.
// tunit3 and tunit4 should match by external references.
const tunit1 = {
'@type': phyx.TaxonomicUnitWrapper.TYPE_TAXON_CONCEPT,
hasName: {
nameComplete: 'Rana luteiventris',
},
};
const tunit2 = {
'@type': [
phyx.TaxonomicUnitWrapper.TYPE_SPECIMEN,
],
nameString: 'Rana luteiventris MVZ225749',
occurrenceID: 'MVZ225749',
};
const tunit3 = {
'@type': phyx.TaxonomicUnitWrapper.TYPE_SPECIMEN,
occurrenceID: 'MVZ225749',
'@id': 'http://arctos.database.museum/guid/MVZ:Herp:225749',
};
const tunit4 = {
'@id': 'http://arctos.database.museum/guid/MVZ:Herp:225749',
};
describe('#matchByNameComplete', function () {
it('should be able to match tunit1 and tunit2 by complete name', function () {
expect(new phyx.TaxonomicUnitMatcher(tunit1, tunit2).matchByExternalReferences()).to.be.false;
expect(new phyx.TaxonomicUnitMatcher(tunit1, tunit2).matchByOccurrenceID()).to.be.false;
expect(new phyx.TaxonomicUnitMatcher(tunit1, tunit2).matchByNameComplete()).to.be.true;
});
});
describe('#matchByExternalReferences', function () {
it('should be able to match tunit3 and tunit4 by external references', function () {
expect(new phyx.TaxonomicUnitMatcher(tunit3, tunit4).matchByExternalReferences()).to.be.true;
expect(new phyx.TaxonomicUnitMatcher(tunit3, tunit4).matchByOccurrenceID()).to.be.false;
expect(new phyx.TaxonomicUnitMatcher(tunit3, tunit4).matchByNameComplete()).to.be.false;
});
});
describe('#matchByOccurrenceID', function () {
it('should be able to match tunit2 and tunit3 by specimen identifiers', function () {
expect(new phyx.TaxonomicUnitMatcher(tunit2, tunit3).matchByExternalReferences()).to.be.false;
expect(new phyx.TaxonomicUnitMatcher(tunit2, tunit3).matchByOccurrenceID()).to.be.true;
expect(new phyx.TaxonomicUnitMatcher(tunit2, tunit3).matchByNameComplete()).to.be.false;
});
});
describe('#matched and #matchReason', function () {
it('should match tunit1 and tunit2 on the basis of identical complete names', function () {
const matcher = new phyx.TaxonomicUnitMatcher(tunit1, tunit2);
expect(matcher.matched).to.be.true;
expect(matcher.matchReason).to.include('share the same complete name');
});
it('should match tunit3 and tunit4 by identical external reference', function () {
const matcher = new phyx.TaxonomicUnitMatcher(tunit3, tunit4);
expect(matcher.matched).to.be.true;
expect(matcher.matchReason).to.include('External reference');
});
it('should match tunit2 and tunit3 by identical specimen identifier', function () {
const matcher = new phyx.TaxonomicUnitMatcher(tunit2, tunit3);
expect(matcher.matched).to.be.true;
expect(matcher.matchReason).to.include('Specimen identifier');
});
});
});