Added some JEST tests to cover skeleton basic features (#4865)

* Added tests to create/get a task with skeletons

* Added some dummy data for tests

* Added a couple of tests

* Aborted extra changes

* Added a couple of put tests

* Removed extra lines

* Added test to check object state logic for complex objects

* Updated eslint config file

* Fixed config

* Removed extra space

* Fixed lint-staged config
main
Boris Sekachev 4 years ago committed by GitHub
parent 99a1be045b
commit 95c20d194f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -3,9 +3,6 @@
// SPDX-License-Identifier: MIT
module.exports = {
env: {
'jest/globals': true,
},
ignorePatterns: [
'.eslintrc.js',
'webpack.config.js',
@ -14,17 +11,10 @@ module.exports = {
'src/3rdparty/**',
'node_modules/**',
'dist/**',
'tests/**/*.js',
],
parserOptions: {
project: './tsconfig.json',
tsconfigRootDir: __dirname,
},
ignorePatterns: ['tests/**/*.js'],
plugins: ['jest'],
rules: {
'jest/no-disabled-tests': 'warn',
'jest/no-focused-tests': 'error',
'jest/no-identical-title': 'error',
'jest/prefer-to-have-length': 'warn',
}
};

@ -65,6 +65,28 @@ describe('Feature: get annotations', () => {
expect(annotations).toHaveLength(1);
expect(annotations[0].shapeType).toBe('ellipse');
});
test('get skeletons with a filter', async () => {
const job = (await window.cvat.jobs.get({ jobID: 40 }))[0];
const annotations = await job.annotations.get(0, false, JSON.parse('[{"and":[{"==":[{"var":"shape"},"skeleton"]}]}]'));
expect(Array.isArray(annotations)).toBeTruthy();
expect(annotations).toHaveLength(2);
for (const object of annotations) {
expect(object.shapeType).toBe('skeleton');
expect(object.elements).toBeInstanceOf(Array);
const label = object.label;
let points = [];
object.elements.forEach((element, idx) => {
expect(element).toBeInstanceOf(cvat.classes.ObjectState);
expect(element.label.id).toBe(label.structure.sublabels[idx].id);
expect(element.shapeType).toBe('points');
points = [...points, ...element.points];
});
expect(points).toEqual(object.points);
}
expect(annotations[0].shapeType).toBe('skeleton');
})
});
describe('Feature: get interpolated annotations', () => {
@ -351,6 +373,68 @@ describe('Feature: put annotations', () => {
zOrder: 0,
})).toThrow(window.cvat.exceptions.ArgumentError);
});
test('put a skeleton shape to a job', async() => {
const job = (await window.cvat.jobs.get({ jobID: 40 }))[0];
const label = job.labels[0];
await job.annotations.clear(true);
await job.annotations.clear();
const skeleton = new window.cvat.classes.ObjectState({
frame: 0,
objectType: window.cvat.enums.ObjectType.SHAPE,
shapeType: window.cvat.enums.ShapeType.SKELETON,
points: [],
label,
elements: label.structure.sublabels.map((sublabel, idx) => ({
frame: 0,
objectType: window.cvat.enums.ObjectType.SHAPE,
shapeType: window.cvat.enums.ShapeType.POINTS,
points: [idx * 10, idx * 10],
label: sublabel,
}))
});
await job.annotations.put([skeleton]);
const annotations = await job.annotations.get(0);
expect(annotations.length).toBe(1);
expect(annotations[0].objectType).toBe(window.cvat.enums.ObjectType.SHAPE);
expect(annotations[0].shapeType).toBe(window.cvat.enums.ShapeType.SKELETON);
for (const element of annotations[0].elements) {
expect(element.objectType).toBe(window.cvat.enums.ObjectType.SHAPE);
expect(element.shapeType).toBe(window.cvat.enums.ShapeType.POINTS);
}
});
test('put a skeleton track to a task', async() => {
const task = (await window.cvat.tasks.get({ id: 40 }))[0];
const label = task.labels[0];
await task.annotations.clear(true);
await task.annotations.clear();
const skeleton = new window.cvat.classes.ObjectState({
frame: 0,
objectType: window.cvat.enums.ObjectType.TRACK,
shapeType: window.cvat.enums.ShapeType.SKELETON,
points: [],
label,
elements: label.structure.sublabels.map((sublabel, idx) => ({
frame: 0,
objectType: window.cvat.enums.ObjectType.TRACK,
shapeType: window.cvat.enums.ShapeType.POINTS,
points: [idx * 10, idx * 10],
label: sublabel,
}))
});
await task.annotations.put([skeleton]);
const annotations = await task.annotations.get(2);
expect(annotations.length).toBe(1);
expect(annotations[0].objectType).toBe(window.cvat.enums.ObjectType.TRACK);
expect(annotations[0].shapeType).toBe(window.cvat.enums.ShapeType.SKELETON);
for (const element of annotations[0].elements) {
expect(element.objectType).toBe(window.cvat.enums.ObjectType.TRACK);
expect(element.shapeType).toBe(window.cvat.enums.ShapeType.POINTS);
}
});
});
describe('Feature: check unsaved changes', () => {
@ -772,6 +856,21 @@ describe('Feature: get statistics', () => {
expect(statistics).toBeInstanceOf(window.cvat.classes.Statistics);
expect(statistics.total.total).toBe(1012);
});
test('get statistics from a job with skeletons', async () => {
const job = (await window.cvat.jobs.get({ jobID: 40 }))[0];
await job.annotations.clear(true);
const statistics = await job.annotations.statistics();
expect(statistics).toBeInstanceOf(window.cvat.classes.Statistics);
expect(statistics.total.total).toBe(30);
const labelName = job.labels[0].name;
expect(statistics.label[labelName].skeleton.shape).toBe(1);
expect(statistics.label[labelName].skeleton.track).toBe(1);
expect(statistics.label[labelName].manually).toBe(2);
expect(statistics.label[labelName].interpolated).toBe(3);
expect(statistics.label[labelName].total).toBe(5);
});
});
describe('Feature: select object', () => {

@ -282,3 +282,40 @@ describe('Feature: delete object', () => {
expect(annotationsAfter).toHaveLength(length - 1);
});
});
describe('Feature: skeletons', () => {
test('lock, hide, occluded, outside for skeletons', async () => {
const job = (await window.cvat.jobs.get({ jobID: 40 }))[0];
let [skeleton] = await job.annotations.get(0, false, JSON.parse('[{"and":[{"==":[{"var":"shape"},"skeleton"]}]}]'));
expect(skeleton.shapeType).toBe('skeleton');
skeleton.lock = true;
skeleton.outside = true;
skeleton.occluded = true;
skeleton.hidden = true;
skeleton = await skeleton.save();
expect(skeleton.lock).toBe(true);
expect(skeleton.outside).toBe(true);
expect(skeleton.occluded).toBe(true);
expect(skeleton.hidden).toBe(true);
expect(skeleton.elements).toBeInstanceOf(Array);
expect(skeleton.elements.length).toBe(skeleton.label.structure.sublabels.length);
for (const element of skeleton.elements) {
expect(element.lock).toBe(true);
expect(element.outside).toBe(true);
expect(element.occluded).toBe(true);
expect(element.hidden).toBe(true);
}
skeleton.elements[0].lock = false;
skeleton.elements[0].outside = false;
skeleton.elements[0].occluded = false;
skeleton.elements[0].hidden = false;
skeleton.elements[0].save();
[skeleton] = await job.annotations.get(0, false, JSON.parse('[{"and":[{"==":[{"var":"shape"},"skeleton"]}]}]'));
expect(skeleton.lock).toBe(false);
expect(skeleton.outside).toBe(false);
expect(skeleton.occluded).toBe(false);
expect(skeleton.hidden).toBe(false);
});
});

@ -18,7 +18,7 @@ describe('Feature: get a list of tasks', () => {
test('get all tasks', async () => {
const result = await window.cvat.tasks.get();
expect(Array.isArray(result)).toBeTruthy();
expect(result).toHaveLength(6);
expect(result).toHaveLength(7);
for (const el of result) {
expect(el).toBeInstanceOf(Task);
}
@ -34,6 +34,33 @@ describe('Feature: get a list of tasks', () => {
expect(result[0].id).toBe(3);
});
test('get a task with skeletons by an id', async () => {
const result = await window.cvat.tasks.get({
id: 40,
});
expect(Array.isArray(result)).toBeTruthy();
expect(result).toHaveLength(1);
expect(result[0]).toBeInstanceOf(Task);
expect(result[0].id).toBe(40);
expect(result[0].labels).toBeInstanceOf(Array);
for (const label of result[0].labels) {
expect(label).toBeInstanceOf(window.cvat.classes.Label);
if (label.type === 'skeleton') {
expect(label.hasParent).toBe(false);
expect(label.structure.sublabels).toBeInstanceOf(Array);
expect(typeof label.structure.svg).toBe('string');
expect(label.structure.svg.length).not.toBe(0);
for (const sublabel of label.structure.sublabels) {
expect(sublabel).toBeInstanceOf(window.cvat.classes.Label);
expect(sublabel.hasParent).toBe(true);
}
}
}
});
test('get a task by an unknown id', async () => {
const result = await window.cvat.tasks.get({
id: 50,
@ -154,12 +181,61 @@ describe('Feature: save a task', () => {
project_id: 2,
bug_tracker: 'bug tracker value',
image_quality: 50,
z_order: true,
});
const result = await task.save();
expect(result.projectId).toBe(2);
});
test('create a new task with skeletons', async () => {
const svgSpec = `
<line x1="65.11705780029297" y1="18.267141342163086" x2="27.49163818359375" y2="39.504600524902344" stroke="black" data-type="edge" data-node-from="3" stroke-width="0.5" data-node-to="5"></line>
<line x1="21.806020736694336" y1="18.099916458129883" x2="65.11705780029297" y2="18.267141342163086" stroke="black" data-type="edge" data-node-from="1" stroke-width="0.5" data-node-to="3"></line>
<line x1="61.10367965698242" y1="40.00627136230469" x2="21.806020736694336" y2="18.099916458129883" stroke="black" data-type="edge" data-node-from="4" stroke-width="0.5" data-node-to="1"></line>
<line x1="44.38127136230469" y1="7.397575378417969" x2="61.10367965698242" y2="40.00627136230469" stroke="black" data-type="edge" data-node-from="2" stroke-width="0.5" data-node-to="4">
</line><line x1="27.49163818359375" y1="39.504600524902344" x2="44.38127136230469" y2="7.397575378417969" stroke="black" data-type="edge" data-node-from="5" stroke-width="0.5" data-node-to="2"></line>
<circle r="1.5" stroke="black" fill="#b3b3b3" cx="21.806020736694336" cy="18.099916458129883" stroke-width="0.1" data-type="element node" data-element-id="1" data-node-id="1" data-label-name="1"></circle>
<circle r="1.5" stroke="black" fill="#b3b3b3" cx="44.38127136230469" cy="7.397575378417969" stroke-width="0.1" data-type="element node" data-element-id="2" data-node-id="2" data-label-name="2"></circle>
<circle r="1.5" stroke="black" fill="#b3b3b3" cx="65.11705780029297" cy="18.267141342163086" stroke-width="0.1" data-type="element node" data-element-id="3" data-node-id="3" data-label-name="3"></circle>
<circle r="1.5" stroke="black" fill="#b3b3b3" cx="61.10367965698242" cy="40.00627136230469" stroke-width="0.1" data-type="element node" data-element-id="4" data-node-id="4" data-label-name="4"></circle>
<circle r="1.5" stroke="black" fill="#b3b3b3" cx="27.49163818359375" cy="39.504600524902344" stroke-width="0.1" data-type="element node" data-element-id="5" data-node-id="5" data-label-name="5"></circle>
`;
const task = new window.cvat.classes.Task({
name: 'task with skeletons',
labels: [{
name: 'star skeleton',
type: 'skeleton',
attributes: [],
svg: svgSpec,
sublabels: [{
name: '1',
type: 'points',
attributes: []
}, {
name: '2',
type: 'points',
attributes: []
}, {
name: '3',
type: 'points',
attributes: []
}, {
name: '4',
type: 'points',
attributes: []
}, {
name: '5',
type: 'points',
attributes: []
}]
}],
project_id: null,
});
const result = await task.save();
expect(typeof result.id).toBe('number');
});
});
describe('Feature: delete a task', () => {

@ -783,6 +783,105 @@ const tasksDummyData = {
stop_frame: 5001,
frame_filter: '',
},
{
url: 'http://localhost:7000/api/tasks/40',
id: 40,
name: 'test',
project_id: null,
mode: 'annotation',
owner: {
url: 'http://localhost:7000/api/users/1',
id: 1,
username: 'admin',
first_name: '',
last_name: '',
},
assignee: null,
bug_tracker: '',
created_date: '2022-08-25T12:10:45.471663Z',
updated_date: '2022-08-25T12:10:45.993989Z',
overlap: 0,
segment_size: 4,
status: 'annotation',
labels: [{
id: 54,
name: 'star skeleton',
color: '#9cb75a',
attributes: [],
type: 'skeleton',
sublabels: [{
id: 55,
name: '1',
color: '#d12345',
attributes: [],
type: 'points',
has_parent: true
}, {
id: 56,
name: '2',
color: '#350dea',
attributes: [],
type: 'points',
has_parent: true
}, {
id: 57,
name: '3',
color: '#479ffe',
attributes: [],
type: 'points',
has_parent: true
}, {
id: 58,
name: '4',
color: '#4a649f',
attributes: [],
type: 'points',
has_parent: true
}, {
id: 59,
name: '5',
color: '#478144',
attributes: [],
type: 'points',
has_parent: true
}],
has_parent: false,
svg:
`<line x1="65.11705780029297" y1="18.267141342163086" x2="27.49163818359375" y2="39.504600524902344" stroke="black" data-type="edge" data-node-from="3" stroke-width="0.5" data-node-to="5"></line>
<line x1="21.806020736694336" y1="18.099916458129883" x2="65.11705780029297" y2="18.267141342163086" stroke="black" data-type="edge" data-node-from="1" stroke-width="0.5" data-node-to="3"></line>
<line x1="61.10367965698242" y1="40.00627136230469" x2="21.806020736694336" y2="18.099916458129883" stroke="black" data-type="edge" data-node-from="4" stroke-width="0.5" data-node-to="1"></line>
<line x1="44.38127136230469" y1="7.397575378417969" x2="61.10367965698242" y2="40.00627136230469" stroke="black" data-type="edge" data-node-from="2" stroke-width="0.5" data-node-to="4"></line>
<line x1="27.49163818359375" y1="39.504600524902344" x2="44.38127136230469" y2="7.397575378417969" stroke="black" data-type="edge" data-node-from="5" stroke-width="0.5" data-node-to="2"></line>
<circle r="1.5" stroke="black" fill="#b3b3b3" cx="21.806020736694336" cy="18.099916458129883" stroke-width="0.1" data-type="element node" data-element-id="1" data-node-id="1" data-label-id="55"></circle>
<circle r="1.5" stroke="black" fill="#b3b3b3" cx="44.38127136230469" cy="7.397575378417969" stroke-width="0.1" data-type="element node" data-element-id="2" data-node-id="2" data-label-id="56"></circle>
<circle r="1.5" stroke="black" fill="#b3b3b3" cx="65.11705780029297" cy="18.267141342163086" stroke-width="0.1" data-type="element node" data-element-id="3" data-node-id="3" data-label-id="57"></circle>
<circle r="1.5" stroke="black" fill="#b3b3b3" cx="61.10367965698242" cy="40.00627136230469" stroke-width="0.1" data-type="element node" data-element-id="4" data-node-id="4" data-label-id="58"></circle>
<circle r="1.5" stroke="black" fill="#b3b3b3" cx="27.49163818359375" cy="39.504600524902344" stroke-width="0.1" data-type="element node" data-element-id="5" data-node-id="5" data-label-id="59"></circle>`
}],
segments: [{
start_frame: 0,
stop_frame: 3,
jobs: [{
url: 'http://localhost:7000/api/jobs/40',
id: 40,
assignee: null,
status: 'annotation',
stage: 'annotation',
state: 'new',
}]
}],
data_chunk_size: 17,
data_compressed_chunk_type: 'imageset',
data_original_chunk_type: 'imageset',
size: 4,
image_quality: 70,
data: 12,
dimension: '2d',
subset: '',
organization: null,
target_storage: null,
source_storage: null
},
{
url: 'http://localhost:7000/api/tasks/3',
id: 3,
@ -2532,6 +2631,230 @@ const taskAnnotationsDummyData = {
],
tracks: [],
},
40: {
version: 0,
tags: [],
shapes: [{
type: 'skeleton',
occluded: false,
outside: false,
z_order: 0,
rotation: 0.0,
points: [],
id: 23,
frame: 0,
label_id: 54,
group: 0,
source: 'manual',
attributes: [],
elements: [{
type: 'points',
occluded: false,
outside: false,
z_order: 0,
rotation: 0.0,
points: [
908.0654296875,
768.8268729552019
],
id: 24,
frame: 0,
label_id: 55,
group: 0,
source: 'manual',
attributes: []
}, {
type: "points",
occluded: false,
outside: false,
z_order: 0,
rotation: 0.0,
points: [
1230.1533057030483,
523.7802734375
],
id: 25,
frame: 0,
label_id: 56,
group: 0,
source: 'manual',
attributes: []
}, {
type: 'points',
occluded: false,
outside: false,
z_order: 0,
rotation: 0.0,
points: [
1525.9969940892624,
772.6557444966547
],
id: 26,
frame: 0,
label_id: 57,
group: 0,
source: 'manual',
attributes: []
}, {
type: 'points',
occluded: false,
outside: false,
z_order: 0,
rotation: 0.0,
points: [
1468.7369236136856,
1270.4066429432623
],
id: 27,
frame: 0,
label_id: 58,
group: 0,
source: 'manual',
attributes: []
}, {
type: 'points',
occluded: false,
outside: false,
z_order: 0,
rotation: 0.0,
points: [
989.1838401839595,
1258.9201156622657
],
id: 28,
frame: 0,
label_id:59,
group: 0,
source: 'manual',
attributes: []
}]
}],
tracks: [{
id: 1,
frame: 0,
label_id: 54,
group: 0,
source: 'manual',
shapes: [{
type: 'skeleton',
occluded: false,
outside: false,
z_order: 0,
rotation: 0.0,
points: [],
id: 1,
frame: 0,
attributes: []
}],
attributes: [],
elements: [{
id: 2,
frame: 0,
label_id: 55,
group: 0,
source: 'manual',
shapes: [{
type: 'points',
occluded: false,
outside: false,
z_order: 0,
rotation: 0.0,
points: [
88.4140625,
332.85145482411826
],
id: 2,
frame: 0,
attributes: []
}],
attributes: []
}, {
id: 3,
frame: 0,
label_id: 56,
group: 0,
source: 'manual',
shapes: [{
type: 'points',
occluded: false,
outside: false,
z_order: 0,
rotation: 0.0,
points: [
437.3386217629577,
96.447265625
],
id: 3,
frame: 0,
attributes: []
}],
attributes: []
}, {
id: 4,
frame: 0,
label_id: 57,
group: 0,
source: 'manual',
shapes: [{
type: 'points',
occluded: false,
outside: false,
z_order: 0,
rotation: 0.0,
points: [
757.8323014937105,
336.54528805456357
],
id: 4,
frame: 0,
attributes: []
}],
attributes: []
}, {
id: 5,
frame: 0,
label_id: 58,
group: 0,
source: 'manual',
shapes: [{
type: 'points',
occluded: false,
outside: false,
z_order: 0,
rotation: 0.0,
points: [
695.8012648051717,
816.7412907822327
],
id: 5,
frame: 0,
attributes: []
}],
attributes: []
}, {
id: 6,
frame: 0,
label_id: 59,
group: 0,
source: 'manual',
shapes: [{
type: 'points',
occluded: false,
outside: false,
z_order: 0,
rotation: 0.0,
points: [
176.29133990867558,
805.659875353811
],
id: 6,
frame: 0,
attributes: []
}],
attributes: []
}]
}]
}
};
const jobAnnotationsDummyData = JSON.parse(JSON.stringify(taskAnnotationsDummyData));
@ -2629,6 +2952,36 @@ const frameMetaDummyData = {
},
],
},
40: {
chunk_size: 17,
size: 4,
image_quality: 70,
start_frame: 0,
stop_frame: 3,
frame_filter: '',
frames: [{
width: 2560,
height: 1703,
name: '1598296101_1033667.jpg',
has_related_context: false
}, {
width: 1600,
height: 1200,
name: '30fdce7f27b9c7b1d50108d7c16d23ef.jpg',
has_related_context: false
}, {
width: 2880,
height: 1800,
name: '567362-ily-comedy-drama-1finding-3.jpg',
has_related_context: false
}, {
width: 1920,
height: 1080,
name: '730443-under-the-sea-wallpapers-1920x1080-windows-10.jpg',
has_related_context: false
}],
deleted_frames: []
},
100: {
chunk_size: 36,
size: 9,

@ -16,9 +16,9 @@ module.exports = (stagedFiles) => {
const eslintFiles = micromatch(stagedFiles, eslintExtensions);
const scssFiles = micromatch(stagedFiles, scssExtensions);
const tests = containsInPath('/tests/', eslintFiles);
const tests = containsInPath('/tests/cypress', eslintFiles);
const cvatData = containsInPath('/cvat-data/', eslintFiles);
const cvatCore = containsInPath('/cvat-core/', eslintFiles);
const cvatCore = containsInPath('/cvat-core/src', eslintFiles);
const cvatCanvas = containsInPath('/cvat-canvas/', eslintFiles);
const cvatCanvas3d = containsInPath('/cvat-canvas3d/', eslintFiles);
const cvatUI = containsInPath('/cvat-ui/', eslintFiles);

Loading…
Cancel
Save