Skip to content

Commit

Permalink
Tidying up tests to be clearer to read in the future
Browse files Browse the repository at this point in the history
  • Loading branch information
ktuite committed Dec 5, 2023
1 parent 3bab8dd commit fb95b8c
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 94 deletions.
8 changes: 4 additions & 4 deletions test/integration/api/datasets.js
Original file line number Diff line number Diff line change
Expand Up @@ -2865,7 +2865,7 @@ describe('datasets and entities', () => {
});
}));

// cb#551 issue, <entity/> tag has no children
// c#551 issue, <entity/> tag has no children
it('should allow update where no label or no properties are updated', testService(async (service, container) => {
const asAlice = await service.login('alice');

Expand Down Expand Up @@ -2930,7 +2930,7 @@ describe('datasets and entities', () => {
});
}));

// cb#552 issue, can't add label to entity update form that previously didnt have label
// c#552 issue, can't add label to entity update form that previously didnt have label
it('should allow update where no label or no properties are updated', testService(async (service) => {
const asAlice = await service.login('alice');

Expand Down Expand Up @@ -2981,8 +2981,8 @@ describe('datasets and entities', () => {
.expect(200);
}));

// cb#553 issue, forms with and without entity label show different fields
// (because entity has type 'unknown' instead of 'structure')
// c#553 issue, forms with and without entity label show different fields
// (because entity was previously type 'unknown' instead of 'structure')
it('should allow update where no label or no properties are updated', testService(async (service) => {
const asAlice = await service.login('alice');

Expand Down
51 changes: 15 additions & 36 deletions test/unit/data/dataset.js
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ describe('parsing dataset from entity block', () => {
should.not.exist(fields[2].propertyName);
}));

it('should figure out type of entity block from form def', async () => {
it('should alawys parse entity field as type structure', async () => {
const form = (entityBlock) => `<?xml version="1.0"?>
<h:html xmlns="http://www.w3.org/2002/xforms" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:jr="http://openrosa.org/javarosa" xmlns:entities="http://www.opendatakit.org/xforms">
<h:head>
Expand All @@ -229,72 +229,51 @@ describe('parsing dataset from entity block', () => {
</h:head>
</h:html>`;

// Entity block has no children
const noChildEntity = '<entity dataset="people" id="" create="" update="" baseVersion=""/>';
await getFormFields(form(noChildEntity)).then((fields) => {
fields[0].name.should.equal('age');
fields[0].path.should.equal('/age');
fields[0].type.should.equal('int');

fields[1].name.should.equal('meta');
fields[1].path.should.equal('/meta');
fields[1].type.should.equal('structure');

fields[2].name.should.equal('entity');
fields[2].path.should.equal('/meta/entity');
fields[2].type.should.equal('structure');
});

// Entity block has no children
const emptyEntity = '<entity dataset="people" id="" create="" update="" baseVersion=""></entity>';
await getFormFields(form(emptyEntity)).then((fields) => {
fields[0].name.should.equal('age');
fields[0].path.should.equal('/age');
fields[0].type.should.equal('int');

fields[1].name.should.equal('meta');
fields[1].path.should.equal('/meta');
fields[1].type.should.equal('structure');
fields[2].name.should.equal('entity');
fields[2].path.should.equal('/meta/entity');
fields[2].type.should.equal('structure');
});

// Entity block has whitespace
const emptyEntityWhitespace = '<entity dataset="people" id="" create="" update="" baseVersion=""> </entity>';
await getFormFields(form(emptyEntityWhitespace)).then((fields) => {
fields[2].name.should.equal('entity');
fields[2].path.should.equal('/meta/entity');
fields[2].type.should.equal('structure');
});

// Entity block is not empty (most common case)
const nonEmptyEntity = '<entity dataset="people" id="" create="" update="" baseVersion=""><label/></entity>';
await getFormFields(form(nonEmptyEntity)).then((fields) => {
fields[0].name.should.equal('age');
fields[0].path.should.equal('/age');
fields[0].type.should.equal('int');

fields[1].name.should.equal('meta');
fields[1].path.should.equal('/meta');
fields[1].type.should.equal('structure');

fields[2].name.should.equal('entity');
fields[2].path.should.equal('/meta/entity');
fields[2].type.should.equal('structure'); // is type structure because it contains a child
fields[2].type.should.equal('structure');

fields[3].name.should.equal('label');
fields[3].path.should.equal('/meta/entity/label');
fields[3].type.should.equal('unknown'); // should possibly be something other than unknown (unknown bc no binding)
fields[3].type.should.equal('unknown'); // label is unknown because there is no child and no bind
});

const nonEmptyLabel = '<entity dataset="people" id="" create="" update="" baseVersion=""><label></label></entity>';
const nonEmptyLabel = '<entity dataset="people" id="" create="" update="" baseVersion=""><label>foo</label></entity>';
await getFormFields(form(nonEmptyLabel)).then((fields) => {
fields[0].name.should.equal('age');
fields[0].path.should.equal('/age');
fields[0].type.should.equal('int');

fields[1].name.should.equal('meta');
fields[1].path.should.equal('/meta');
fields[1].type.should.equal('structure');

fields[2].name.should.equal('entity');
fields[2].path.should.equal('/meta/entity');
fields[2].type.should.equal('structure'); // is type structure because it contains a child

fields[3].name.should.equal('label');
fields[3].path.should.equal('/meta/entity/label');
fields[3].type.should.equal('unknown'); // should possibly be something other than unknown (unknown bc no children and no binding)
fields[3].type.should.equal('unknown'); // type unknown because no child node and no bind
});
});
});
Expand Down
87 changes: 33 additions & 54 deletions test/unit/data/submission.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,10 @@ describe('submission field streamer', () => {
should.config.checkProtoEql = true;
});

// true, false (entity has attributes and is included. other fields like /meta is structural but has no attributes so it is not included)
it('should include structural fields with attributes', (done) => {
fieldsFor(testData.forms.simpleEntity).then((fields) =>
submissionXmlToFieldStream(fields, testData.instances.simpleEntity.one, true).pipe(toObjects((error, result) => {
submissionXmlToFieldStream(fields, testData.instances.simpleEntity.one, true, false).pipe(toObjects((error, result) => {
result.should.eql([
{ field: new MockField({ order: 4, name: 'entity', path: '/meta/entity', type: 'structure', attrs: {
create: '1',
Expand All @@ -84,9 +85,10 @@ describe('submission field streamer', () => {
})));
});

// false, false (entity has attributes but it is structural so not included)
it('should not include structural fields', (done) => {
fieldsFor(testData.forms.simpleEntity).then((fields) =>
submissionXmlToFieldStream(fields, testData.instances.simpleEntity.one, false).pipe(toObjects((error, result) => {
submissionXmlToFieldStream(fields, testData.instances.simpleEntity.one, false, false).pipe(toObjects((error, result) => {
result.should.eql([
{ field: new MockField({ order: 5, name: 'label', path: '/meta/entity/label', type: 'unknown' }), text: 'Alice (88)' },
{ field: new MockField({ order: 0, name: 'name', path: '/name', type: 'string', propertyName: 'first_name' }), text: 'Alice' },
Expand All @@ -97,6 +99,7 @@ describe('submission field streamer', () => {
})));
});

// true, true (entity has attributes, age is empty)
it('should include structural elements with attributes and empty nodes', (done) => {
fieldsFor(testData.forms.simpleEntity).then((fields) =>
submissionXmlToFieldStream(fields, testData.instances.simpleEntity.one.replace('<age>88</age>', '<age></age>'), true, true).pipe(toObjects((error, result) => {
Expand All @@ -115,57 +118,21 @@ describe('submission field streamer', () => {
})));
});

it('should not return structural nodes when they have no attributes', async () => {
const form = `<?xml version="1.0"?>
<h:html xmlns="http://www.w3.org/2002/xforms" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:jr="http://openrosa.org/javarosa" xmlns:entities="http://www.opendatakit.org/xforms">
<h:head>
<model entities:entities-version="2023.1.0">
<instance>
<data id="brokenForm" orx:version="1.0">
<person>
<age/>
</person>
<meta>
<entity>
<label/>
</entity>
</meta>
</data>
<other/>
</instance>
<bind nodeset="/data/person/age" type="int" entities:saveto="age"/>
</model>
</h:head>
</h:html>`;

const fields = await fieldsFor(form);
fields.length.should.equal(5);

const sub = `<data xmlns:jr="http://openrosa.org/javarosa" xmlns:entities="http://www.opendatakit.org/xforms" id="brokenForm" version="1.0">
<meta>
<instanceID>one</instanceID>
<orx:instanceName>one</orx:instanceName>
<entity>
<label>foo</label>
</entity>
</meta>
<person>
<age>88</age>
</person>
</data>`;

await submissionXmlToFieldStream(fields, sub, true, true).pipe(toObjects((error, result) => {
result.should.eql([
// /meta/entity field is not present because it has no attribtues, which means it isn't used later by parseSubmissionXml
// which is good, because it wont try to access non-existent entity attributes.
// the entity system data stuff will be set to null and will log an appropriate error.
{ field: new MockField({ order: 4, name: 'label', path: '/meta/entity/label', type: 'unknown' }), text: 'foo' },
// /person is also not included because it is a structural node with no attributes. but it's child is included.
{ field: new MockField({ order: 1, name: 'age', path: '/person/age', type: 'int', propertyName: 'age' }), text: '88' },
]);
}));
// false, true (age is empty here. other fields like name and hometown are not empty and are returned as normal.)
it('should include empty nodes but no structural nodes', (done) => {
fieldsFor(testData.forms.simpleEntity).then((fields) =>
submissionXmlToFieldStream(fields, testData.instances.simpleEntity.one.replace('<age>88</age>', '<age></age>'), false, true).pipe(toObjects((error, result) => {
result.should.eql([
{ field: new MockField({ order: 5, name: 'label', path: '/meta/entity/label', type: 'unknown' }), text: 'Alice (88)' },
{ field: new MockField({ order: 0, name: 'name', path: '/name', type: 'string', propertyName: 'first_name' }), text: 'Alice' },
{ field: new MockField({ order: 1, name: 'age', path: '/age', type: 'int', propertyName: 'age' }), text: '' },
{ field: new MockField({ order: 2, name: 'hometown', path: '/hometown', type: 'string' }), text: 'Chicago' }
]);
done();
})));
});

// related to issue c#551 where <entity> block had no children so extracting the attributes was breaking.
it('should handle attributes on entity tag with no children', async () => {
const form = `<?xml version="1.0"?>
<h:html xmlns="http://www.w3.org/2002/xforms" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:jr="http://openrosa.org/javarosa" xmlns:entities="http://www.opendatakit.org/xforms">
Expand All @@ -174,20 +141,25 @@ describe('submission field streamer', () => {
<instance>
<data id="brokenForm" orx:version="1.0">
<age/>
<location>
<hometown></hometown>
</location>
<meta>
<entity dataset="people" id="" create="" update="" baseVersion="" />
</meta>
</data>
<other/>
</instance>
<bind nodeset="/data/age" type="int" entities:saveto="age"/>
<bind nodeset="/data/location/hometown" type="string" entities:saveto="hometown"/>
</model>
</h:head>
</h:html>`;

// This is all the fields in the form including structural fields
const fields = await fieldsFor(form);
fields.map(f => f.name).should.eql(['age', 'meta', 'entity']);
fields.map(f => f.type).should.eql(['int', 'structure', 'structure']);
fields.map(f => f.name).should.eql(['age', 'location', 'hometown', 'meta', 'entity']);
fields.map(f => f.type).should.eql(['int', 'structure', 'string', 'structure', 'structure']);

const sub = `<data xmlns:jr="http://openrosa.org/javarosa" xmlns:entities="http://www.opendatakit.org/xforms" id="brokenForm" version="1.0">
<meta>
Expand All @@ -196,17 +168,24 @@ describe('submission field streamer', () => {
<entity baseVersion="1" dataset="people" id="12345678-1234-4123-8234-123456789abc" update="1"/>
</meta>
<age>88</age>
<location>
<hometown></hometown>
</location>
</data>`;

// This is where we use the full field list above to pull out only the fields that are relevant to entity parsing
// - <entity> with its attribuets
// - all leaf nodes even if they are empty
await submissionXmlToFieldStream(fields, sub, true, true).pipe(toObjects((error, result) => {
result.should.eql([
{ field: new MockField({ order: 2, name: 'entity', path: '/meta/entity', type: 'structure', attrs: {
{ field: new MockField({ order: 4, name: 'entity', path: '/meta/entity', type: 'structure', attrs: {
update: '1',
baseVersion: '1',
dataset: 'people',
id: '12345678-1234-4123-8234-123456789abc'
} }), text: null },
{ field: new MockField({ order: 0, name: 'age', path: '/age', type: 'int', propertyName: 'age' }), text: '88' },
{ field: new MockField({ order: 2, name: 'hometown', path: '/location/hometown', type: 'string', propertyName: 'hometown' }), text: '' },
]);
}));
});
Expand Down

0 comments on commit fb95b8c

Please sign in to comment.