Browse Source

Tests fixed.

pull/297/head
Sebastian 8 years ago
parent
commit
24b7b8af20
  1. 85
      src/Squidex.Domain.Apps.Core.Operations/ConvertContent/FieldConverters.cs
  2. 44
      src/Squidex/app/framework/angular/forms/forms-helper.ts
  3. 1
      src/Squidex/app/framework/declarations.ts
  4. 1
      src/Squidex/app/framework/internal.ts
  5. 40
      src/Squidex/app/framework/state.ts
  6. 42
      src/Squidex/app/framework/utils/lazy.spec.ts
  7. 24
      src/Squidex/app/framework/utils/lazy.ts
  8. 6
      src/Squidex/app/shared/services/schemas.service.spec.ts
  9. 48
      src/Squidex/app/shared/services/schemas.service.ts
  10. 10
      src/Squidex/app/shared/state/contents.forms.spec.ts
  11. 75
      src/Squidex/app/shared/state/contents.forms.ts
  12. 12
      tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/FieldConvertersTests.cs

85
src/Squidex.Domain.Apps.Core.Operations/ConvertContent/FieldConverters.cs

@ -233,54 +233,61 @@ namespace Squidex.Domain.Apps.Core.ConvertContent
{ {
if (field is IArrayField arrayField) if (field is IArrayField arrayField)
{ {
var result = new ContentFieldData();
foreach (var partition in data) foreach (var partition in data)
{ {
if (partition.Value is JArray jArray) if (!(partition.Value is JArray jArray))
{ {
for (var i = 0; i < jArray.Count; i++) continue;
{ }
if (jArray[i] is JObject item)
{
var result = new JObject();
foreach (var kvp in item) var newArray = new JArray();
{
var nestedField = fieldResolver(arrayField, kvp.Key);
if (nestedField == null) foreach (JObject item in jArray.OfType<JObject>())
{ {
continue; var newItem = new JObject();
}
foreach (var kvp in item)
{
var nestedField = fieldResolver(arrayField, kvp.Key);
var newValue = kvp.Value; if (nestedField == null)
{
continue;
}
var isUnset = false; var newValue = kvp.Value;
if (converters != null) var isUnset = false;
{
foreach (var converter in converters)
{
newValue = converter(newValue, nestedField);
if (ReferenceEquals(newValue, Value.Unset))
{
isUnset = true;
break;
}
}
}
if (!isUnset) if (converters != null)
{
foreach (var converter in converters)
{
newValue = converter(newValue, nestedField);
if (ReferenceEquals(newValue, Value.Unset))
{ {
result.Add(keyResolver(nestedField), newValue); isUnset = true;
break;
} }
} }
}
jArray[i] = result; if (!isUnset)
{
newItem.Add(keyResolver(nestedField), newValue);
} }
} }
newArray.Add(newItem);
} }
result.Add(partition.Key, newArray);
} }
return result;
} }
return data; return data;
@ -293,7 +300,7 @@ namespace Squidex.Domain.Apps.Core.ConvertContent
{ {
if (!(field is IArrayField)) if (!(field is IArrayField))
{ {
ContentFieldData result = null; var result = new ContentFieldData();
foreach (var partition in data) foreach (var partition in data)
{ {
@ -315,21 +322,13 @@ namespace Squidex.Domain.Apps.Core.ConvertContent
} }
} }
if (result != null || isUnset || !ReferenceEquals(newValue, partition.Value)) if (!isUnset)
{ {
if (result == null) result.Add(partition.Key, newValue);
{
result = new ContentFieldData();
}
if (!isUnset)
{
result.Add(partition.Key, newValue);
}
} }
} }
return result ?? data; return result;
} }
return data; return data;

44
src/Squidex/app/framework/angular/forms/forms-helper.ts

@ -0,0 +1,44 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { AbstractControl, FormArray, FormGroup } from '@angular/forms';
import { Types } from '@app/framework/internal';
export const formControls = (form: AbstractControl): AbstractControl[] => {
if (Types.is(form, FormGroup)) {
return Object.values(form.controls);
} else if (Types.is(form, FormArray)) {
return form.controls;
} else {
return [];
}
};
export const fullValue = (form: AbstractControl): any => {
if (Types.is(form, FormGroup)) {
const groupValue = {};
for (let key in form.controls) {
if (form.controls.hasOwnProperty(key)) {
groupValue[key] = fullValue(form.controls[key]);
}
}
return groupValue;
} else if (Types.is(form, FormArray)) {
const arrayValue = [];
for (let child of form.controls) {
arrayValue.push(fullValue(child));
}
return arrayValue;
} else {
return form.value;
}
};

1
src/Squidex/app/framework/declarations.ts

@ -14,6 +14,7 @@ export * from './angular/forms/dropdown.component';
export * from './angular/forms/file-drop.directive'; export * from './angular/forms/file-drop.directive';
export * from './angular/forms/focus-on-init.directive'; export * from './angular/forms/focus-on-init.directive';
export * from './angular/forms/form-error.component'; export * from './angular/forms/form-error.component';
export * from './angular/forms/forms-helper';
export * from './angular/forms/iframe-editor.component'; export * from './angular/forms/iframe-editor.component';
export * from './angular/forms/indeterminate-value.directive'; export * from './angular/forms/indeterminate-value.directive';
export * from './angular/forms/jscript-editor.component'; export * from './angular/forms/jscript-editor.component';

1
src/Squidex/app/framework/internal.ts

@ -23,6 +23,7 @@ export * from './utils/date-time';
export * from './utils/duration'; export * from './utils/duration';
export * from './utils/error'; export * from './utils/error';
export * from './utils/immutable-array'; export * from './utils/immutable-array';
export * from './utils/lazy';
export * from './utils/math-helper'; export * from './utils/math-helper';
export * from './utils/modal-view'; export * from './utils/modal-view';
export * from './utils/pager'; export * from './utils/pager';

40
src/Squidex/app/framework/state.ts

@ -5,11 +5,11 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/ */
import { AbstractControl, FormArray, FormGroup } from '@angular/forms'; import { AbstractControl } from '@angular/forms';
import { BehaviorSubject, Observable } from 'rxjs'; import { BehaviorSubject, Observable } from 'rxjs';
import { ErrorDto } from './utils/error'; import { ErrorDto, Types } from '@app/framework/internal';
import { Types } from './utils/types'; import { fullValue} from './angular/forms/forms-helper';
export interface FormState { export interface FormState {
submitted: boolean; submitted: boolean;
@ -17,34 +17,6 @@ export interface FormState {
error?: string; error?: string;
} }
export class Lazy<T> {
private valueSet = false;
private valueField: T;
public get value(): T {
if (!this.valueSet) {
this.valueField = this.factory();
this.valueSet = true;
}
return this.valueField;
}
constructor(
private readonly factory: () => T
) {
}
}
export const formControls = (form: AbstractControl): AbstractControl[] => {
if (Types.is(form, FormGroup)) {
return Object.values(form.controls);
} else if (Types.is(form, FormArray)) {
return form.controls;
} else {
return [];
}
};
export class Form<T extends AbstractControl> { export class Form<T extends AbstractControl> {
private readonly state = new State<FormState>({ submitted: false }); private readonly state = new State<FormState>({ submitted: false });
@ -85,7 +57,7 @@ export class Form<T extends AbstractControl> {
this.state.next({ submitted: true }); this.state.next({ submitted: true });
if (this.form.valid) { if (this.form.valid) {
const value = this.form.value; const value = fullValue(this.form);
this.disable(); this.disable();
@ -133,6 +105,10 @@ export class Model {
const clone = Object.assign(Object.create(Object.getPrototypeOf(this)), this, values); const clone = Object.assign(Object.create(Object.getPrototypeOf(this)), this, values);
if (Types.isFunction(clone.onCloned)) {
clone.onCloned();
}
return clone; return clone;
} }
} }

42
src/Squidex/app/framework/utils/lazy.spec.ts

@ -0,0 +1,42 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { Lazy } from './lazy';
describe('Lazy', () => {
it('should provider value', () => {
const lazy = new Lazy(() => 1);
expect(lazy.value).toBe(1);
});
it('should call delegate once', () => {
let called = 0;
const lazy = new Lazy(() => {
called++;
return 13;
});
expect(lazy.value).toBe(13);
expect(lazy.value).toBe(13);
expect(called).toBe(1);
});
it('should call delegate once when returned undefined', () => {
let called = 0;
const lazy = new Lazy(() => {
called++;
return undefined;
});
expect(lazy.value).toBeUndefined();
expect(lazy.value).toBeUndefined();
expect(called).toBe(1);
});
});

24
src/Squidex/app/framework/utils/lazy.ts

@ -0,0 +1,24 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
export class Lazy<T> {
private valueSet = false;
private valueField: T;
public get value(): T {
if (!this.valueSet) {
this.valueField = this.factory();
this.valueSet = true;
}
return this.valueField;
}
constructor(
private readonly factory: () => T
) {
}
}

6
src/Squidex/app/shared/services/schemas.service.spec.ts

@ -158,6 +158,7 @@ describe('SchemasService', () => {
{ {
fieldId: 101, fieldId: 101,
name: 'field101', name: 'field101',
isLocked: true,
isHidden: true, isHidden: true,
isDisabled: true, isDisabled: true,
properties: { properties: {
@ -167,6 +168,7 @@ describe('SchemasService', () => {
{ {
fieldId: 102, fieldId: 102,
name: 'field102', name: 'field102',
isLocked: true,
isHidden: true, isHidden: true,
isDisabled: true, isDisabled: true,
properties: { properties: {
@ -293,8 +295,8 @@ describe('SchemasService', () => {
new Version('2'), new Version('2'),
[ [
new RootFieldDto(11, 'field11', createProperties('Array'), 'language', true, true, true, [ new RootFieldDto(11, 'field11', createProperties('Array'), 'language', true, true, true, [
new NestedFieldDto(101, 'field101', createProperties('String'), 11, true, true), new NestedFieldDto(101, 'field101', createProperties('String'), 11, true, true, true),
new NestedFieldDto(102, 'field102', createProperties('Number'), 11, true, true) new NestedFieldDto(102, 'field102', createProperties('Number'), 11, true, true, true)
]), ]),
new RootFieldDto(12, 'field12', createProperties('Assets'), 'language', true, true, true), new RootFieldDto(12, 'field12', createProperties('Assets'), 'language', true, true, true),
new RootFieldDto(13, 'field13', createProperties('Boolean'), 'language', true, true, true), new RootFieldDto(13, 'field13', createProperties('Boolean'), 'language', true, true, true),

48
src/Squidex/app/shared/services/schemas.service.ts

@ -16,7 +16,6 @@ import {
ApiUrlConfig, ApiUrlConfig,
DateTime, DateTime,
HTTP, HTTP,
Lazy,
Model, Model,
StringHelper, StringHelper,
Version, Version,
@ -26,10 +25,8 @@ import {
import { createProperties, FieldPropertiesDto } from './schemas.types'; import { createProperties, FieldPropertiesDto } from './schemas.types';
export class SchemaDto extends Model { export class SchemaDto extends Model {
private readonly displayNameValue = new Lazy((() => StringHelper.firstNonEmpty(this.properties.label, this.name)));
public get displayName() { public get displayName() {
return this.displayNameValue.value; return StringHelper.firstNonEmpty(this.properties.label, this.name);
} }
constructor( constructor(
@ -53,28 +50,8 @@ export class SchemaDto extends Model {
} }
export class SchemaDetailsDto extends SchemaDto { export class SchemaDetailsDto extends SchemaDto {
private inlineEditableFieldsValue = new Lazy(() => this.listFields.filter(x => x.isInlineEditable)); public listFields: RootFieldDto[];
private listFieldsValue = new Lazy(() => { public listFieldsEditable: RootFieldDto[];
let fields = this.fields.filter(x => x.properties.isListField);
if (fields.length === 0 && this.fields.length > 0) {
fields = [this.fields[0]];
}
if (fields.length === 0) {
fields = [<any>{ properties: {} }];
}
return fields;
});
public get inlineEditableFields() {
return this.inlineEditableFieldsValue.value;
}
public get listFields() {
return this.listFieldsValue.value;
}
constructor(id: string, name: string, category: string, properties: SchemaPropertiesDto, isPublished: boolean, created: DateTime, createdBy: string, lastModified: DateTime, lastModifiedBy: string, version: Version, constructor(id: string, name: string, category: string, properties: SchemaPropertiesDto, isPublished: boolean, created: DateTime, createdBy: string, lastModified: DateTime, lastModifiedBy: string, version: Version,
public readonly fields: RootFieldDto[], public readonly fields: RootFieldDto[],
@ -85,6 +62,25 @@ export class SchemaDetailsDto extends SchemaDto {
public readonly scriptChange?: string public readonly scriptChange?: string
) { ) {
super(id, name, category, properties, isPublished, created, createdBy, lastModified, lastModifiedBy, version); super(id, name, category, properties, isPublished, created, createdBy, lastModified, lastModifiedBy, version);
this.onCloned();
}
protected onCloned() {
if (this.fields) {
let fields = this.fields.filter(x => x.properties.isListField);
if (fields.length === 0 && this.fields.length > 0) {
fields = [this.fields[0]];
}
if (fields.length === 0) {
fields = [<any>{ properties: {} }];
}
this.listFields = fields;
this.listFieldsEditable = fields.filter(x => x.isInlineEditable);
}
} }
public with(value: Partial<SchemaDetailsDto>): SchemaDetailsDto { public with(value: Partial<SchemaDetailsDto>): SchemaDetailsDto {

10
src/Squidex/app/shared/state/contents.forms.spec.ts

@ -129,7 +129,7 @@ describe('ArrayField', () => {
}); });
it('should format to asset count', () => { it('should format to asset count', () => {
expect(FieldFormatter.format(field, [1, 2, 3])).toBe('3 Items(s)'); expect(FieldFormatter.format(field, [1, 2, 3])).toBe('3 Item(s)');
}); });
it('should return zero formatting if other type', () => { it('should return zero formatting if other type', () => {
@ -250,15 +250,15 @@ describe('DateTimeField', () => {
}); });
it('should return calculated date when Today for DateFieldProperties', () => { it('should return calculated date when Today for DateFieldProperties', () => {
Object.assign(field.properties, { calculatedFieldDefaultValue: 'Today' }); Object.assign(field.properties, { calculatedDefaultValue: 'Today' });
expect((<any>field).properties.getFieldDefaultValue(now)).toEqual('2017-10-12'); expect(FieldDefaultValue.get(field, now)).toEqual('2017-10-12');
}); });
it('should return calculated date when Now for DateFieldProperties', () => { it('should return calculated date when Now for DateFieldProperties', () => {
Object.assign(field.properties, { calculatedFieldDefaultValue: 'Now' }); Object.assign(field.properties, { calculatedDefaultValue: 'Now' });
expect((<any>field).properties.getFieldDefaultValue(now)).toEqual('2017-10-12T16:30:10Z'); expect(FieldDefaultValue.get(field, now)).toEqual('2017-10-12T16:30:10Z');
}); });
}); });

75
src/Squidex/app/shared/state/contents.forms.ts

@ -43,7 +43,7 @@ export class FieldFormatter implements FieldPropertiesVisitor<string> {
} }
public static format(field: FieldDto, value: any) { public static format(field: FieldDto, value: any) {
if (!value) { if (value === null || value === undefined) {
return ''; return '';
} }
@ -259,8 +259,25 @@ export class FieldValidatorsFactory implements FieldPropertiesVisitor<ValidatorF
} }
export class FieldDefaultValue implements FieldPropertiesVisitor<any> { export class FieldDefaultValue implements FieldPropertiesVisitor<any> {
public static get(field: FieldDto) { constructor(
return field.properties.accept(new FieldDefaultValue()); private readonly now?: DateTime
) {
}
public visitDateTime(properties: DateTimeFieldPropertiesDto): any {
const now = this.now || DateTime.now();
if (properties.calculatedDefaultValue === 'Now') {
return now.toUTCStringFormat('YYYY-MM-DDTHH:mm:ss') + 'Z';
} else if (properties.calculatedDefaultValue === 'Today') {
return now.toUTCStringFormat('YYYY-MM-DD');
} else {
return properties.defaultValue;
}
}
public static get(field: FieldDto, now?: DateTime) {
return field.properties.accept(new FieldDefaultValue(now));
} }
public visitArray(properties: ArrayFieldPropertiesDto): any { public visitArray(properties: ArrayFieldPropertiesDto): any {
@ -275,10 +292,6 @@ export class FieldDefaultValue implements FieldPropertiesVisitor<any> {
return properties.defaultValue; return properties.defaultValue;
} }
public visitDateTime(properties: DateTimeFieldPropertiesDto): any {
return null;
}
public visitGeolocation(properties: GeolocationFieldPropertiesDto): any { public visitGeolocation(properties: GeolocationFieldPropertiesDto): any {
return null; return null;
} }
@ -388,16 +401,24 @@ export class EditContentForm extends Form<FormGroup> {
const fieldValue = value ? value[field.name] || {} : {}; const fieldValue = value ? value[field.name] || {} : {};
const addControls = (key: string, language: AppLanguageDto | null) => { const addControls = (key: string, language: AppLanguageDto | null) => {
const languageValue = fieldValue[key]; const partitionValue = fieldValue[key];
const languageForm = new FormArray([]);
if (Types.isArray(languageValue)) { let partitionForm = <FormArray>fieldForm.controls[key];
for (let i = 0; i < languageValue.length; i++) {
this.addArrayItem(field, language, languageForm); if (!partitionForm) {
} partitionForm = new FormArray([]);
fieldForm.setControl(key, partitionForm);
} }
fieldForm.setControl(key, languageForm); const length = Types.isArray(partitionValue) ? partitionValue.length : 0;
while (partitionForm.controls.length < length) {
this.addArrayItem(field, language, partitionForm);
}
while (partitionForm.controls.length > length) {
partitionForm.removeAt(partitionForm.length - 1);
}
}; };
if (field.isLocalizable) { if (field.isLocalizable) {
@ -432,19 +453,21 @@ export class EditContentForm extends Form<FormGroup> {
continue; continue;
} }
if (field.properties.fieldType === 'Array') { if (field.isArray) {
fieldForm.enable();
for (let partitionForm of formControls(fieldForm)) { for (let partitionForm of formControls(fieldForm)) {
for (let nested of field.nested) { for (let itemForm of formControls(partitionForm)) {
const nestedForm = partitionForm.get(nested.name); for (let nested of field.nested) {
const nestedForm = itemForm.get(nested.name);
if (!nestedForm) { if (!nestedForm) {
continue; continue;
} }
if (nested.isDisabled) { if (nested.isDisabled) {
nestedForm.disable(); nestedForm.disable({ onlySelf: true });
} else { }
nestedForm.enable();
} }
} }
} }
@ -464,7 +487,7 @@ export class PatchContentForm extends Form<FormGroup> {
) { ) {
super(new FormGroup({})); super(new FormGroup({}));
for (let field of this.schema.inlineEditableFields) { for (let field of this.schema.listFieldsEditable) {
const validators = FieldValidatorsFactory.createValidators(field, this.language.isOptional); const validators = FieldValidatorsFactory.createValidators(field, this.language.isOptional);
this.form.setControl(field.name, new FormControl(undefined, validators)); this.form.setControl(field.name, new FormControl(undefined, validators));
@ -477,7 +500,7 @@ export class PatchContentForm extends Form<FormGroup> {
if (result) { if (result) {
const request = {}; const request = {};
for (let field of this.schema.inlineEditableFields) { for (let field of this.schema.listFieldsEditable) {
const value = result[field.name]; const value = result[field.name];
if (field.isLocalizable) { if (field.isLocalizable) {

12
tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/FieldConvertersTests.cs

@ -27,18 +27,6 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
Fields.Number(1, "field1"), Fields.Number(1, "field1"),
Fields.Number(2, "field2").Hide()); Fields.Number(2, "field2").Hide());
[Fact]
public void Should_return_same_object_for_value_conversion_if_nothing_converted()
{
var input =
new ContentFieldData()
.AddValue("iv", new JObject());
var actual = FieldConverters.ForValues()(input, stringInvariantField);
Assert.Same(input, actual);
}
[Fact] [Fact]
public void Should_filter_for_value_conversion() public void Should_filter_for_value_conversion()
{ {

Loading…
Cancel
Save