Skip to content

Commit

Permalink
Add tests for rename field
Browse files Browse the repository at this point in the history
  • Loading branch information
cdauth committed Mar 27, 2024
1 parent 0e22ed2 commit 3c1b59c
Show file tree
Hide file tree
Showing 5 changed files with 263 additions and 65 deletions.
209 changes: 205 additions & 4 deletions integration-tests/src/types.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -480,10 +480,211 @@ test("Delete type (existing lines)", async () => {
});
});

// Rename field, make sure markers are updated
// Rename field, make sure lines are updated
// Rename dropdown option, make sure markers are updated
// Rename dropdown option, make sure lines are updated
test("Rename field (marker type)", async () => {
const client = await openClient();

await createTemporaryPad(client, { createDefaultTypes: false }, async (createPadData, padData, result) => {
const type = await client.addType({
name: "Test type",
type: "marker",
fields: [
{ name: "Field 1", type: "input" },
{ name: "Field 2", type: "input" }
]
});

await client.updateBbox({ top: 1, right: 1, bottom: -1, left: -1, zoom: 0 }); // To have marker in bbox

const marker = await client.addMarker({
lat: 0,
lon: 0,
typeId: type.id,
data: {
"Field 1": "value 1",
"Field 2": "value 2"
}
});

const onMarker = vi.fn();
client.on("marker", onMarker);

await client.editType({
id: type.id,
fields: [
{ oldName: "Field 1", name: "Field 1 new", type: "input" },
{ name: "Field 2", type: "input" }
]
});

await retry(() => {
expect(onMarker).toBeCalledTimes(1);
});

expect(client.markers[marker.id].data).toEqual({
"Field 1 new": "value 1",
"Field 2": "value 2"
});
});
});

test("Rename field (line type)", async () => {
const client = await openClient();

await createTemporaryPad(client, { createDefaultTypes: false }, async (createPadData, padData, result) => {
const type = await client.addType({
name: "Test type",
type: "line",
fields: [
{ name: "Field 1", type: "input" },
{ name: "Field 2", type: "input" }
]
});

const line = await client.addLine({
routePoints: [
{ lat: 0, lon: 0 },
{ lat: 1, lon: 1 }
],
typeId: type.id,
data: {
"Field 1": "value 1",
"Field 2": "value 2"
}
});

const onLine = vi.fn();
client.on("line", onLine);

await client.editType({
id: type.id,
fields: [
{ oldName: "Field 1", name: "Field 1 new", type: "input" },
{ name: "Field 2", type: "input" }
]
});

await retry(() => {
expect(onLine).toBeCalledTimes(1);
});

expect(client.lines[line.id].data).toEqual({
"Field 1 new": "value 1",
"Field 2": "value 2"
});
});
});

test("Rename dropdown option (marker type)", async () => {
const client = await openClient();

await createTemporaryPad(client, { createDefaultTypes: false }, async (createPadData, padData, result) => {
const type = await client.addType({
name: "Test type",
type: "marker",
fields: [
{ name: "Dropdown", type: "dropdown", options: [ { value: "Option 1" }, { value: "Option 2" } ] },
]
});

await client.updateBbox({ top: 1, right: 1, bottom: -1, left: -1, zoom: 0 }); // To have marker in bbox

const marker1 = await client.addMarker({
lat: 0,
lon: 0,
typeId: type.id,
data: {
"Dropdown": "Option 1"
}
});

const marker2 = await client.addMarker({
lat: 0,
lon: 0,
typeId: type.id,
data: {
"Dropdown": "Option 2"
}
});

const onMarker = vi.fn();
client.on("marker", onMarker);

await client.editType({
id: type.id,
fields: [
{ name: "Dropdown", type: "dropdown", options: [ { value: "Option 1" }, { oldValue: "Option 2", value: "Option 2 new" } ] }
]
});

await retry(() => {
expect(onMarker).toBeCalledTimes(1);
});

expect(client.markers[marker1.id].data).toEqual({
"Dropdown": "Option 1"
});
expect(client.markers[marker2.id].data).toEqual({
"Dropdown": "Option 2 new"
});
});
});

test("Rename dropdown option (line type)", async () => {
const client = await openClient();

await createTemporaryPad(client, { createDefaultTypes: false }, async (createPadData, padData, result) => {
const type = await client.addType({
name: "Test type",
type: "line",
fields: [
{ name: "Dropdown", type: "dropdown", options: [ { value: "Option 1" }, { value: "Option 2" } ] },
]
});

const line1 = await client.addLine({
routePoints: [
{ lat: 0, lon: 0 },
{ lat: 1, lon: 1 }
],
typeId: type.id,
data: {
"Dropdown": "Option 1"
}
});

const line2 = await client.addLine({
routePoints: [
{ lat: 0, lon: 0 },
{ lat: 1, lon: 1 }
],
typeId: type.id,
data: {
"Dropdown": "Option 2"
}
});

const onLine = vi.fn();
client.on("line", onLine);

await client.editType({
id: type.id,
fields: [
{ name: "Dropdown", type: "dropdown", options: [ { value: "Option 1" }, { oldValue: "Option 2", value: "Option 2 new" } ] }
]
});

await retry(() => {
expect(onLine).toBeCalledTimes(1);
});

expect(client.lines[line1.id].data).toEqual({
"Dropdown": "Option 1"
});
expect(client.lines[line2.id].data).toEqual({
"Dropdown": "Option 2 new"
});
});
});

// New marker (default values) is created with default settings
// New line (default values) is created with default settings
Expand Down
24 changes: 12 additions & 12 deletions server/src/database/line.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,27 +234,27 @@ export default class DatabaseLines {
throw new Error(`Cannot use ${newType.type} type for line.`);
}

const update = {
...resolveUpdateLine(originalLine, data, newType),
routePoints: data.routePoints || originalLine.routePoints,
mode: (data.mode ?? originalLine.mode) || ""
};
const update = resolveUpdateLine(originalLine, data, newType);

let routeInfo: RouteInfo | undefined;
if((update.mode == "track" && update.trackPoints) || !isEqual(update.routePoints, originalLine.routePoints) || update.mode != originalLine.mode)
routeInfo = await calculateRouteForLine(update, trackPointsFromRoute);
if((update.mode == "track" && update.trackPoints) || (update.routePoints && !isEqual(update.routePoints, originalLine.routePoints)) || (update.mode != null && update.mode != originalLine.mode))
routeInfo = await calculateRouteForLine({ ...originalLine, ...update }, trackPointsFromRoute);

Object.assign(update, mapValues(routeInfo, (val) => val == null ? null : val)); // Use null instead of undefined
delete update.trackPoints; // They came if mode is track

const newLine = await this._db.helpers._updatePadObject<Line>("Line", originalLine.padId, originalLine.id, update, noHistory);
if (Object.keys(update).length > 0) {
const newLine = await this._db.helpers._updatePadObject<Line>("Line", originalLine.padId, originalLine.id, update, noHistory);

this._db.emit("line", originalLine.padId, newLine);
this._db.emit("line", originalLine.padId, newLine);

if(routeInfo)
await this._setLinePoints(originalLine.padId, originalLine.id, routeInfo.trackPoints);
if(routeInfo)
await this._setLinePoints(originalLine.padId, originalLine.id, routeInfo.trackPoints);

return newLine;
return newLine;
} else {
return originalLine;
}
}

async _setLinePoints(padId: PadId, lineId: ID, trackPoints: Point[], _noEvent?: boolean): Promise<void> {
Expand Down
30 changes: 17 additions & 13 deletions server/src/database/marker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,21 +123,25 @@ export default class DatabaseMarkers {

const update = resolveUpdateMarker(originalMarker, data, newType);

const result = await this._db.helpers._updatePadObject<Marker>("Marker", originalMarker.padId, originalMarker.id, update, noHistory);

this._db.emit("marker", originalMarker.padId, result);
if (Object.keys(update).length > 0) {
const result = await this._db.helpers._updatePadObject<Marker>("Marker", originalMarker.padId, originalMarker.id, update, noHistory);

this._db.emit("marker", originalMarker.padId, result);

if (update.lat != null && update.lon != null && update.ele === undefined) {
getElevationForPoint({ lat: update.lat, lon: update.lon }).then(async (ele) => {
if (ele != null) {
await this.updateMarker(originalMarker.padId, originalMarker.id, { ele }, true);
}
}).catch((err) => {
console.warn("Error updating marker elevation", err);
});
}

if (update.lat != null && update.lon != null && update.ele === undefined) {
getElevationForPoint({ lat: update.lat, lon: update.lon }).then(async (ele) => {
if (ele != null) {
await this.updateMarker(originalMarker.padId, originalMarker.id, { ele }, true);
}
}).catch((err) => {
console.warn("Error updating marker elevation", err);
});
return result;
} else {
return originalMarker;
}

return result;
}

async deleteMarker(padId: PadId, markerId: ID): Promise<Marker> {
Expand Down
31 changes: 28 additions & 3 deletions server/src/database/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,16 +125,41 @@ export default class DatabaseTypes {
return createdType;
}

async updateType(padId: PadId, typeId: ID, data: Omit<Type<CRU.UPDATE_VALIDATED>, "id">, _doNotUpdateStyles?: boolean): Promise<Type> {
async updateType(padId: PadId, typeId: ID, data: Omit<Type<CRU.UPDATE_VALIDATED>, "id">): Promise<Type> {
const rename: Record<string, { name?: string, values?: Record<string, string> }> = {};
for(const field of (data.fields || [])) {
if(field.oldName && field.oldName != field.name)
rename[field.oldName] = { name: field.name };

if(field.options) {
for(const option of field.options) {
if(option.oldValue && option.oldValue != option.value) {
if(!rename[field.oldName || field.name])
rename[field.oldName || field.name] = { };
if(!rename[field.oldName || field.name].values)
rename[field.oldName || field.name].values = { };

rename[field.oldName || field.name].values![option.oldValue] = option.value;
}

delete option.oldValue;
}
}

delete field.oldName;
}

if (data.idx != null) {
await this._freeTypeIdx(padId, typeId, data.idx);
}

const result = await this._db.helpers._updatePadObject<Type>("Type", padId, typeId, data);
this._db.emit("type", result.padId, result);

if(!_doNotUpdateStyles)
await this.recalculateObjectStylesForType(result.padId, typeId, result.type == "line");
if(Object.keys(rename).length > 0)
await this._db.helpers.renameObjectDataField(padId, result.id, rename, result.type == "line");

await this.recalculateObjectStylesForType(result.padId, typeId, result.type == "line");

return result;
}
Expand Down
34 changes: 1 addition & 33 deletions server/src/socket/socket-v2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -357,39 +357,7 @@ export class SocketConnectionV2 extends SocketConnection {
if (!isPadId(this.padId))
throw new Error("No map opened.");

const rename: Record<string, { name?: string, values?: Record<string, string> }> = {};
for(const field of (data.fields || [])) {
if(field.oldName && field.oldName != field.name)
rename[field.oldName] = { name: field.name };

if(field.options) {
for(const option of field.options) {
if(option.oldValue && option.oldValue != option.value) {
if(!rename[field.oldName || field.name])
rename[field.oldName || field.name] = { };
if(!rename[field.oldName || field.name].values)
rename[field.oldName || field.name].values = { };

rename[field.oldName || field.name].values![option.oldValue] = option.value;
}

delete option.oldValue;
}
}

delete field.oldName;
}

// We first update the type (without updating the styles). If that succeeds, we rename the data fields.
// Only then we update the object styles (as they often depend on the field values).
const newType = await this.database.types.updateType(this.padId, data.id, data, false)

if(Object.keys(rename).length > 0)
await this.database.helpers.renameObjectDataField(this.padId, data.id, rename, newType.type == "line");

await this.database.types.recalculateObjectStylesForType(newType.padId, newType.id, newType.type == "line")

return newType;
return await this.database.types.updateType(this.padId, data.id, data);
},

deleteType: async (data) => {
Expand Down

0 comments on commit 3c1b59c

Please sign in to comment.