mirror of
https://github.com/noodlapp/noodl.git
synced 2026-01-12 15:22:52 +01:00
Initial commit
Co-Authored-By: Eric Tuvesson <eric.tuvesson@gmail.com> Co-Authored-By: mikaeltellhed <2311083+mikaeltellhed@users.noreply.github.com> Co-Authored-By: kotte <14197736+mrtamagotchi@users.noreply.github.com> Co-Authored-By: Anders Larsson <64838990+anders-topp@users.noreply.github.com> Co-Authored-By: Johan <4934465+joolsus@users.noreply.github.com> Co-Authored-By: Tore Knudsen <18231882+torekndsn@users.noreply.github.com> Co-Authored-By: victoratndl <99176179+victoratndl@users.noreply.github.com>
This commit is contained in:
4
packages/noodl-editor/tests/project/index.ts
Normal file
4
packages/noodl-editor/tests/project/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
// export * from './projectcloudsync'; //this is the old version control system
|
||||
export * from './projectimport';
|
||||
export * from './projectmodel';
|
||||
export * from './projectvalidator';
|
||||
358
packages/noodl-editor/tests/project/projectimport.js
Normal file
358
packages/noodl-editor/tests/project/projectimport.js
Normal file
@@ -0,0 +1,358 @@
|
||||
const ProjectImporter = require('@noodl-utils/projectimporter');
|
||||
const FileSystem = require('@noodl-utils/filesystem');
|
||||
const { ProjectModel } = require('@noodl-models/projectmodel');
|
||||
const Utils = require('@noodl-utils/utils');
|
||||
const Process = require('process');
|
||||
const ncp = require('ncp').ncp;
|
||||
const fs = require('fs');
|
||||
const { projectFromDirectory } = require('@noodl-models/projectmodel.editor');
|
||||
|
||||
const remote = require('@electron/remote');
|
||||
const App = remote.app;
|
||||
|
||||
describe('Project import and export unit tests', function () {
|
||||
function expectFilesToExist(direntry, paths, callback) {
|
||||
var filesToCheck = paths.length;
|
||||
var success = true;
|
||||
|
||||
function done() {
|
||||
filesToCheck--;
|
||||
if (filesToCheck === 0) callback(success);
|
||||
}
|
||||
|
||||
for (var i in paths) {
|
||||
FileSystem.instance.fileExists(direntry + '/' + paths[i], function (exists) {
|
||||
if (!exists) success = false;
|
||||
done();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
it('can import project with styles and variants', function (done) {
|
||||
ProjectImporter.instance.listComponentsAndDependencies(
|
||||
Process.cwd() + '/tests/testfs/import_proj5',
|
||||
function (imports) {
|
||||
expect(imports.styles.colors).toEqual([
|
||||
{
|
||||
name: 'Primary'
|
||||
},
|
||||
{
|
||||
name: 'Light Gray'
|
||||
},
|
||||
{
|
||||
name: 'Dark Gray'
|
||||
},
|
||||
{
|
||||
name: 'Primary Dark'
|
||||
},
|
||||
{
|
||||
name: 'Dark'
|
||||
},
|
||||
{
|
||||
name: 'Primary Light'
|
||||
}
|
||||
]);
|
||||
|
||||
expect(imports.styles.text).toEqual([
|
||||
{
|
||||
name: 'Body Text',
|
||||
fileDependencies: ['fonts/Roboto/Roboto-Regular.ttf']
|
||||
},
|
||||
{
|
||||
name: 'Button Label',
|
||||
fileDependencies: ['fonts/Roboto/Roboto-Regular.ttf']
|
||||
},
|
||||
{
|
||||
name: 'Label Text',
|
||||
fileDependencies: ['fonts/Roboto/Roboto-Regular.ttf']
|
||||
}
|
||||
]);
|
||||
|
||||
expect(imports.variants).toEqual([
|
||||
{
|
||||
name: 'Basic',
|
||||
typename: 'net.noodl.controls.button',
|
||||
fileDependencies: ['fonts/Roboto/Roboto-Medium.ttf'],
|
||||
styleDependencies: {
|
||||
colors: ['Primary', 'Primary Light', 'Primary Dark', 'Light Gray'],
|
||||
text: ['Button Label']
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Search Field',
|
||||
typename: 'net.noodl.controls.textinput',
|
||||
fileDependencies: ['fonts/Roboto/Roboto-Medium.ttf'],
|
||||
styleDependencies: {
|
||||
colors: ['Light Gray', 'Dark', 'Primary'],
|
||||
text: ['Body Text', 'Label Text']
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('can check for collissions (with styles and variants)', function (done) {
|
||||
projectFromDirectory(Process.cwd() + '/tests/testfs/import_proj5', function (project) {
|
||||
ProjectModel.instance = project;
|
||||
|
||||
// Now check for collistions with project to import
|
||||
ProjectImporter.instance.listComponentsAndDependencies(
|
||||
Process.cwd() + '/tests/testfs/import_proj5',
|
||||
function (imports) {
|
||||
ProjectImporter.instance.checkForCollisions(imports, function (collisions) {
|
||||
expect(collisions.components.length).toBe(2);
|
||||
expect(collisions.modules.length).toBe(1);
|
||||
expect(collisions.resources.length).toBe(13);
|
||||
expect(collisions.variants.length).toBe(2);
|
||||
expect(collisions.styles.colors.length).toBe(6);
|
||||
expect(collisions.styles.text.length).toBe(3);
|
||||
done();
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('can list components and dependencies', function (done) {
|
||||
ProjectImporter.instance.listComponentsAndDependencies(
|
||||
Process.cwd() + '/tests/testfs/import_proj1',
|
||||
function (imports) {
|
||||
console.log(imports);
|
||||
expect(imports.components).toEqual([
|
||||
{
|
||||
name: '/comp1',
|
||||
dependencies: [],
|
||||
fileDependencies: ['assets/bear.jpg'],
|
||||
styleDependencies: {
|
||||
text: [],
|
||||
colors: []
|
||||
},
|
||||
variantDependencies: []
|
||||
},
|
||||
{
|
||||
name: '/comp2',
|
||||
dependencies: [],
|
||||
fileDependencies: [],
|
||||
styleDependencies: {
|
||||
text: [],
|
||||
colors: []
|
||||
},
|
||||
variantDependencies: []
|
||||
},
|
||||
{
|
||||
name: '/Main',
|
||||
dependencies: ['/comp1'],
|
||||
fileDependencies: ['Fontfabric - Nexa-Bold.otf'],
|
||||
styleDependencies: {
|
||||
text: [],
|
||||
colors: []
|
||||
},
|
||||
variantDependencies: []
|
||||
}
|
||||
]);
|
||||
|
||||
expect(imports.styles).toEqual({
|
||||
text: [],
|
||||
colors: []
|
||||
});
|
||||
|
||||
//the order of these are different on mac and windows, so sort them
|
||||
imports.resources.sort((a, b) => {
|
||||
return a.name.localeCompare(b.name);
|
||||
});
|
||||
|
||||
expect(imports.resources).toEqual([
|
||||
{
|
||||
name: 'assets/bear.jpg'
|
||||
},
|
||||
{
|
||||
name: 'assets/bikeyellowbuilding.jpg'
|
||||
},
|
||||
{
|
||||
name: 'bear.jpg'
|
||||
},
|
||||
{
|
||||
name: 'Fontfabric - Nexa-Bold.otf'
|
||||
}
|
||||
]);
|
||||
|
||||
expect(imports.variants).toEqual([]);
|
||||
expect(imports.modules).toEqual([]);
|
||||
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('can check for collissions (1)', function (done) {
|
||||
projectFromDirectory(Process.cwd() + '/tests/testfs/import_proj1', function (project) {
|
||||
ProjectModel.instance = project;
|
||||
|
||||
// Now check for collistions with project to import
|
||||
ProjectImporter.instance.listComponentsAndDependencies(
|
||||
Process.cwd() + '/tests/testfs/import_proj2',
|
||||
function (imports) {
|
||||
ProjectImporter.instance.checkForCollisions(imports, function (collisions) {
|
||||
expect(collisions.components.length).toBe(1);
|
||||
expect(collisions.components[0].name).toBe('/Main');
|
||||
|
||||
expect(collisions.resources).toEqual([]);
|
||||
expect(collisions.modules).toEqual([]);
|
||||
expect(collisions.variants).toEqual([]);
|
||||
expect(collisions.styles).toEqual({
|
||||
colors: [],
|
||||
text: []
|
||||
});
|
||||
|
||||
done();
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('can check for collissions (2)', function (done) {
|
||||
projectFromDirectory(Process.cwd() + '/tests/testfs/import_proj1', function (project) {
|
||||
ProjectModel.instance = project;
|
||||
|
||||
// Now check for collistions with project to import
|
||||
ProjectImporter.instance.listComponentsAndDependencies(
|
||||
Process.cwd() + '/tests/testfs/import_proj3',
|
||||
function (imports) {
|
||||
ProjectImporter.instance.checkForCollisions(imports, function (collisions) {
|
||||
expect(collisions.resources).toEqual([
|
||||
{
|
||||
name: 'Fontfabric - Nexa-Bold.otf'
|
||||
},
|
||||
{
|
||||
name: 'assets/bear.jpg'
|
||||
}
|
||||
]);
|
||||
|
||||
expect(collisions.components).toEqual([]);
|
||||
expect(collisions.modules).toEqual([]);
|
||||
expect(collisions.variants).toEqual([]);
|
||||
expect(collisions.styles).toEqual({
|
||||
colors: [],
|
||||
text: []
|
||||
});
|
||||
|
||||
done();
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('can import project with files', function (done) {
|
||||
var tempDir = App.getPath('temp') + '/noodlunittests-' + Utils.guid() + '/';
|
||||
FileSystem.instance.makeDirectory(tempDir, function (r) {
|
||||
if (r.result !== 'success') {
|
||||
throw 'gaah';
|
||||
}
|
||||
|
||||
ncp(Process.cwd() + '/tests/testfs/import_proj1', tempDir + '/import_proj1', function (err) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
projectFromDirectory(tempDir + '/import_proj1', function (project) {
|
||||
ProjectModel.instance = project;
|
||||
|
||||
ProjectImporter.instance.listComponentsAndDependencies(
|
||||
Process.cwd() + '/tests/testfs/import_proj3',
|
||||
function (imports) {
|
||||
ProjectImporter.instance.import(Process.cwd() + '/tests/testfs/import_proj3', imports, function () {
|
||||
expect(ProjectModel.instance.getComponentWithName('/Main2')).not.toBe(undefined);
|
||||
|
||||
// Check that files have been copied properly
|
||||
expectFilesToExist(
|
||||
tempDir + '/import_proj1',
|
||||
['assets/bear.jpg', 'Fontfabric - Nexa-Bold.otf', 'newfile.jpg'],
|
||||
function (success) {
|
||||
expect(success).toBe(true);
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('can import project with styles, variants and modules', function (done) {
|
||||
var tempDir = App.getPath('temp') + '/noodlunittests-' + Utils.guid() + '/';
|
||||
FileSystem.instance.makeDirectory(tempDir, function (r) {
|
||||
if (r.result !== 'success') {
|
||||
throw 'gaah';
|
||||
}
|
||||
|
||||
ncp(Process.cwd() + '/tests/testfs/import_proj1', tempDir + '/import_proj1', function (err) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
projectFromDirectory(tempDir + '/import_proj1', function (project) {
|
||||
ProjectModel.instance = project;
|
||||
|
||||
ProjectImporter.instance.listComponentsAndDependencies(
|
||||
Process.cwd() + '/tests/testfs/import_proj5',
|
||||
function (imports) {
|
||||
ProjectImporter.instance.import(Process.cwd() + '/tests/testfs/import_proj5', imports, function () {
|
||||
const styles = ProjectModel.instance.getMetaData('styles');
|
||||
expect(Object.keys(styles.colors).sort()).toEqual([
|
||||
'Dark',
|
||||
'Dark Gray',
|
||||
'Light Gray',
|
||||
'Primary',
|
||||
'Primary Dark',
|
||||
'Primary Light'
|
||||
]);
|
||||
expect(Object.keys(styles.text).sort()).toEqual(['Body Text', 'Button Label', 'Label Text']);
|
||||
|
||||
expect(
|
||||
ProjectModel.instance.findVariant('Basic', {
|
||||
localName: 'net.noodl.controls.button'
|
||||
})
|
||||
).not.toBe(undefined);
|
||||
expect(
|
||||
ProjectModel.instance.findVariant('Search Field', {
|
||||
localName: 'net.noodl.controls.textinput'
|
||||
})
|
||||
).not.toBe(undefined);
|
||||
|
||||
expect(fs.existsSync(tempDir + '/import_proj1/noodl_modules/material-icons')).toBe(true);
|
||||
|
||||
done();
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('ignores .git', function (done) {
|
||||
const path = Process.cwd() + '/tests/testfs/import_proj4/';
|
||||
|
||||
//add a .git folder with a file inside
|
||||
FileSystem.instance.makeDirectorySync(path + '.git');
|
||||
FileSystem.instance.writeFileSync(path + '.git/test', 'test');
|
||||
|
||||
ProjectImporter.instance.listComponentsAndDependencies(path, (imports) => {
|
||||
expect(imports.components.length).toBe(1);
|
||||
expect(imports.resources.length).toBe(0);
|
||||
|
||||
//remove the .git folder
|
||||
FileSystem.instance.removeFileSync(path + '.git/test');
|
||||
FileSystem.instance.removeDirectoryRecursiveSync(path + '.git');
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
175
packages/noodl-editor/tests/project/projectmodel.js
Normal file
175
packages/noodl-editor/tests/project/projectmodel.js
Normal file
@@ -0,0 +1,175 @@
|
||||
const NodeLibrary = require('@noodl-models/nodelibrary').NodeLibrary;
|
||||
const { ProjectModel } = require('@noodl-models/projectmodel');
|
||||
const WarningsModel = require('@noodl-models/warningsmodel').WarningsModel;
|
||||
|
||||
describe('Project model tests', function () {
|
||||
it('can upgrade from 0 to 1', function () {
|
||||
var p = ProjectModel.fromJSON(project0);
|
||||
|
||||
// Project upgrader from 0 to 1 should replace = types with * types
|
||||
var json = JSON.stringify(p.toJSON());
|
||||
expect(json.indexOf('=')).toBe(-1);
|
||||
expect(json.match(/\*/g).length).toBe(2);
|
||||
});
|
||||
|
||||
xit('can apply patch', function () {
|
||||
var p = ProjectModel.fromJSON(project1);
|
||||
|
||||
// Apply patch
|
||||
p.applyPatch({
|
||||
askPermission: false,
|
||||
notifyUser: false,
|
||||
nodePatches: [
|
||||
{
|
||||
nodeId: 'A',
|
||||
typename: 'group',
|
||||
version: 3,
|
||||
params: {
|
||||
x: 20,
|
||||
alignX: null,
|
||||
alignY: 'top'
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
expect(p.findNodeWithId('A').type instanceof NodeLibrary.BasicNodeType).toBe(true);
|
||||
expect(p.findNodeWithId('A').type.name).toBe('group');
|
||||
expect(p.findNodeWithId('A').version).toBe(3);
|
||||
expect(p.findNodeWithId('A').getParameter('x')).toBe(20);
|
||||
expect(p.findNodeWithId('A').getParameter('alignX')).toBe(undefined);
|
||||
expect(p.findNodeWithId('A').getParameter('alignY')).toBe('top');
|
||||
|
||||
// Ask permission, should not apply patch immediately
|
||||
p.applyPatch({
|
||||
key: 'a',
|
||||
askPermission: true,
|
||||
notifyUser: false,
|
||||
nodePatches: [
|
||||
{
|
||||
nodeId: 'A',
|
||||
params: {
|
||||
alignY: 'bottom'
|
||||
}
|
||||
}
|
||||
],
|
||||
dismissPatches: [
|
||||
{
|
||||
nodeId: 'A',
|
||||
params: {
|
||||
alignY: null
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
expect(WarningsModel.instance.warnings['/']['/']['patch-a']).not.toBe(undefined); // Warnings should be generated
|
||||
expect(p.findNodeWithId('A').getParameter('alignY')).toBe('top');
|
||||
|
||||
// Emulate user clicking patch
|
||||
WarningsModel.instance.warnings['/']['/']['patch-a'].warning.onPatch();
|
||||
expect(p.findNodeWithId('A').getParameter('alignY')).toBe('bottom');
|
||||
|
||||
// Ask permission, should not apply patch immediately
|
||||
p.applyPatch({
|
||||
key: 'a',
|
||||
askPermission: true,
|
||||
notifyUser: false,
|
||||
nodePatches: [],
|
||||
dismissPatches: [
|
||||
{
|
||||
nodeId: 'A',
|
||||
params: {
|
||||
alignY: null
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
// Emulate user clicking dismiss
|
||||
WarningsModel.instance.warnings['/']['/']['patch-a'].warning.onDismiss();
|
||||
expect(p.findNodeWithId('A').getParameter('alignY')).toBe(undefined);
|
||||
});
|
||||
|
||||
xit('can apply settings patch', function () {
|
||||
var p = ProjectModel.fromJSON(project1);
|
||||
|
||||
p.applyPatch({
|
||||
askPermission: false,
|
||||
notifyUser: false,
|
||||
settingsPatch: {
|
||||
s1: null,
|
||||
s2: 'Wohoo'
|
||||
}
|
||||
});
|
||||
|
||||
expect(p.getSettings().s1).toBe(undefined);
|
||||
expect(p.getSettings().s2).toBe('Wohoo');
|
||||
});
|
||||
|
||||
// Project that should be patched
|
||||
var project1 = {
|
||||
components: [
|
||||
{
|
||||
name: 'comp1',
|
||||
graph: {
|
||||
roots: [
|
||||
{
|
||||
id: 'A',
|
||||
type: 'image',
|
||||
parameters: {
|
||||
x: 10,
|
||||
alignX: 'left'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
settings: {
|
||||
s1: 'Hello'
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
// Old project model that should be upgraded
|
||||
var project0 = {
|
||||
components: [
|
||||
{
|
||||
name: 'comp1',
|
||||
graph: {
|
||||
roots: [
|
||||
{
|
||||
id: 'ES-1',
|
||||
type: 'Event Sender',
|
||||
ports: [
|
||||
{
|
||||
type: '=',
|
||||
name: 'a port'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'comp2',
|
||||
graph: {
|
||||
roots: [
|
||||
{
|
||||
id: 'ES-1',
|
||||
type: 'Event Sender',
|
||||
ports: [
|
||||
{
|
||||
type: {
|
||||
name: '='
|
||||
},
|
||||
name: 'a port'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
});
|
||||
47
packages/noodl-editor/tests/project/projectvalidator.js
Normal file
47
packages/noodl-editor/tests/project/projectvalidator.js
Normal file
@@ -0,0 +1,47 @@
|
||||
var ProjectValidator = require('@noodl-utils/projectvalidator');
|
||||
|
||||
// Project settings
|
||||
describe('Project validator', function () {
|
||||
it('can validate missing components', function () {
|
||||
const proj = {};
|
||||
|
||||
const validator = new ProjectValidator();
|
||||
validator.validate(proj);
|
||||
expect(validator.hasErrors()).toBe(true);
|
||||
expect(validator.errors[0].msg).toBe('Project is missing name');
|
||||
expect(validator.errors[1].msg).toBe('Project is missing components');
|
||||
});
|
||||
|
||||
it('can validate dangling connections and fix', function () {
|
||||
const proj = {
|
||||
name: 'C',
|
||||
components: [
|
||||
{
|
||||
name: 'A',
|
||||
graph: {
|
||||
roots: [],
|
||||
connections: [
|
||||
{
|
||||
fromId: 'a',
|
||||
fromProperty: 'hej',
|
||||
toId: 'b',
|
||||
toProperty: 'hej'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const validator = new ProjectValidator();
|
||||
validator.validate(proj);
|
||||
expect(validator.hasErrors()).toBe(true);
|
||||
expect(validator.errors[0].msg).toBe('Dangling connection at A missing source missing target ');
|
||||
|
||||
validator.fix();
|
||||
validator.clearErrors();
|
||||
validator.validate(proj);
|
||||
expect(validator.hasErrors()).toBe(false);
|
||||
expect(proj.components[0].graph.connections.length).toBe(0);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user