mirror of
https://github.com/nasa/openmct.git
synced 2025-03-14 16:26:50 +00:00
[New Edit Mode] Added additional tests for Seamless Edit Mode #464
This commit is contained in:
parent
c71aa43581
commit
2c4d53883a
@ -50,6 +50,7 @@ define(
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
|
||||
mockScope = jasmine.createSpyObj(
|
||||
"$scope",
|
||||
[ "$on", "$watch" ]
|
||||
@ -82,11 +83,11 @@ define(
|
||||
);
|
||||
mockDomainObject = jasmine.createSpyObj(
|
||||
"domainObject",
|
||||
[ "getId", "getCapability", "getModel", "useCapability" ]
|
||||
[ "getId", "hasCapability", "getCapability", "getModel", "useCapability" ]
|
||||
);
|
||||
mockNextObject = jasmine.createSpyObj(
|
||||
"nextObject",
|
||||
[ "getId", "getCapability", "getModel", "useCapability" ]
|
||||
[ "getId", "hasCapability", "getCapability", "getModel", "useCapability" ]
|
||||
);
|
||||
|
||||
mockObjectService.getObjects.andReturn(mockPromise({
|
||||
@ -98,9 +99,13 @@ define(
|
||||
mockDomainObject.useCapability.andReturn(mockPromise([
|
||||
mockNextObject
|
||||
]));
|
||||
|
||||
mockNextObject.useCapability.andReturn(undefined);
|
||||
mockNextObject.getId.andReturn("next");
|
||||
mockNextObject.hasCapability.andReturn(false);
|
||||
|
||||
mockDomainObject.getId.andReturn("mine");
|
||||
mockDomainObject.hasCapability.andReturn(false);
|
||||
|
||||
controller = new BrowseController(
|
||||
mockScope,
|
||||
|
@ -38,7 +38,7 @@
|
||||
{
|
||||
"key": "edit",
|
||||
"implementation": "actions/EditAction.js",
|
||||
"depends": [ "$location", "$q", "navigationService", "$log" ],
|
||||
"depends": [ "$q", "navigationService", "$log" ],
|
||||
"description": "Edit this object.",
|
||||
"category": "view-control",
|
||||
"glyph": "p"
|
||||
@ -67,7 +67,7 @@
|
||||
"implementation": "actions/SaveAction.js",
|
||||
"name": "Save",
|
||||
"description": "Save changes made to these objects.",
|
||||
"depends": [ "$location", "navigationService" ],
|
||||
"depends": [ "navigationService" ],
|
||||
"priority": "mandatory"
|
||||
},
|
||||
{
|
||||
@ -76,7 +76,7 @@
|
||||
"implementation": "actions/CancelAction.js",
|
||||
"name": "Cancel",
|
||||
"description": "Discard changes made to these objects.",
|
||||
"depends": [ "$location", "navigationService" ]
|
||||
"depends": ["navigationService" ]
|
||||
}
|
||||
],
|
||||
"policies": [
|
||||
|
@ -33,9 +33,8 @@ define(
|
||||
* @memberof platform/commonUI/edit
|
||||
* @implements {Action}
|
||||
*/
|
||||
function CancelAction($location, navigationService, context) {
|
||||
function CancelAction(navigationService, context) {
|
||||
this.domainObject = context.domainObject;
|
||||
this.$location = $location;
|
||||
this.navigationService = navigationService;
|
||||
}
|
||||
|
||||
@ -47,7 +46,6 @@ define(
|
||||
*/
|
||||
CancelAction.prototype.perform = function () {
|
||||
var domainObject = this.domainObject,
|
||||
$location = this.$location,
|
||||
navigationService = this.navigationService;
|
||||
|
||||
// Look up the object's "editor.completion" capability;
|
||||
@ -82,7 +80,7 @@ define(
|
||||
CancelAction.appliesTo = function (context) {
|
||||
var domainObject = (context || {}).domainObject;
|
||||
return domainObject !== undefined &&
|
||||
domainObject.hasCapability("editor");
|
||||
domainObject.getCapability('status').get('editing');
|
||||
};
|
||||
|
||||
return CancelAction;
|
||||
|
@ -46,7 +46,7 @@ define(
|
||||
* @constructor
|
||||
* @implements {Action}
|
||||
*/
|
||||
function EditAction($location, $q, navigationService, $log, context) {
|
||||
function EditAction($q, navigationService, $log, context) {
|
||||
var domainObject = (context || {}).domainObject;
|
||||
|
||||
// We cannot enter Edit mode if we have no domain object to
|
||||
@ -63,17 +63,20 @@ define(
|
||||
}
|
||||
|
||||
this.domainObject = domainObject;
|
||||
this.$location = $location;
|
||||
this.navigationService = navigationService;
|
||||
this.$q = $q;
|
||||
}
|
||||
|
||||
EditAction.prototype.createEditableObject = function(domainObject) {
|
||||
return new EditableDomainObject(domainObject, this.$q);
|
||||
};
|
||||
|
||||
/**
|
||||
* Enter edit mode.
|
||||
*/
|
||||
EditAction.prototype.perform = function () {
|
||||
this.domainObject.getCapability('status').set('editing', true);
|
||||
this.navigationService.setNavigation(new EditableDomainObject(this.domainObject, this.$q));
|
||||
this.navigationService.setNavigation(this.createEditableObject(this.domainObject));
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -34,9 +34,8 @@ define(
|
||||
* @implements {Action}
|
||||
* @memberof platform/commonUI/edit
|
||||
*/
|
||||
function SaveAction($location, navigationService, context) {
|
||||
function SaveAction(navigationService, context) {
|
||||
this.domainObject = (context || {}).domainObject;
|
||||
this.$location = $location;
|
||||
this.navigationService = navigationService;
|
||||
}
|
||||
|
||||
@ -49,8 +48,6 @@ define(
|
||||
*/
|
||||
SaveAction.prototype.perform = function () {
|
||||
var domainObject = this.domainObject,
|
||||
$location = this.$location,
|
||||
urlService = this.urlService,
|
||||
navigationService = this.navigationService;
|
||||
|
||||
// Invoke any save behavior introduced by the editor capability;
|
||||
@ -65,10 +62,6 @@ define(
|
||||
// UI, which will have been pushed atop the Browise UI.)
|
||||
function returnToBrowse(nonEditableDomainObject) {
|
||||
navigationService.setNavigation(nonEditableDomainObject);
|
||||
/*return $location.path(urlService.urlForLocation(
|
||||
"browse",
|
||||
domainObject
|
||||
));*/
|
||||
}
|
||||
|
||||
return doSave().then(returnToBrowse);
|
||||
@ -83,7 +76,7 @@ define(
|
||||
SaveAction.appliesTo = function (context) {
|
||||
var domainObject = (context || {}).domainObject;
|
||||
return domainObject !== undefined &&
|
||||
domainObject.hasCapability("editor");
|
||||
domainObject.getCapability("status").get("editing");
|
||||
};
|
||||
|
||||
return SaveAction;
|
||||
|
@ -104,7 +104,7 @@ define(
|
||||
return saveChanges().then(function(){
|
||||
domainObject.getCapability('status').set('editing', false);
|
||||
return domainObject;
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@ -131,7 +131,7 @@ define(
|
||||
|
||||
EditorCapability.prototype.getDomainObject = function () {
|
||||
return this.domainObject;
|
||||
}
|
||||
};
|
||||
|
||||
return EditorCapability;
|
||||
}
|
||||
|
@ -27,10 +27,11 @@ define(
|
||||
"use strict";
|
||||
|
||||
describe("The Cancel action", function () {
|
||||
var mockLocation,
|
||||
mockDomainObject,
|
||||
var mockDomainObject,
|
||||
mockCapabilities,
|
||||
mockEditorCapability,
|
||||
mockUrlService,
|
||||
mockStatusCapability,
|
||||
mockNavigationService,
|
||||
actionContext,
|
||||
action;
|
||||
|
||||
@ -43,45 +44,50 @@ define(
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
mockLocation = jasmine.createSpyObj(
|
||||
"$location",
|
||||
[ "path" ]
|
||||
);
|
||||
mockDomainObject = jasmine.createSpyObj(
|
||||
"domainObject",
|
||||
[ "getCapability", "hasCapability" ]
|
||||
[ "getCapability" ]
|
||||
);
|
||||
mockEditorCapability = jasmine.createSpyObj(
|
||||
"editor",
|
||||
[ "save", "cancel" ]
|
||||
);
|
||||
mockUrlService = jasmine.createSpyObj(
|
||||
"urlService",
|
||||
["urlForLocation"]
|
||||
mockStatusCapability = jasmine.createSpyObj(
|
||||
"status",
|
||||
[ "get"]
|
||||
);
|
||||
|
||||
mockNavigationService = jasmine.createSpyObj(
|
||||
"navigationService",
|
||||
["setNavigation"]
|
||||
);
|
||||
mockCapabilities = {
|
||||
"editor": mockEditorCapability,
|
||||
"status": mockStatusCapability
|
||||
};
|
||||
actionContext = {
|
||||
domainObject: mockDomainObject
|
||||
};
|
||||
|
||||
mockDomainObject.hasCapability.andReturn(true);
|
||||
mockDomainObject.getCapability.andReturn(mockEditorCapability);
|
||||
mockEditorCapability.cancel.andReturn(mockPromise(true));
|
||||
mockDomainObject.getCapability.andCallFake(function(capability){
|
||||
return mockCapabilities[capability];
|
||||
});
|
||||
|
||||
action = new CancelAction(mockLocation, mockUrlService, actionContext);
|
||||
mockEditorCapability.cancel.andReturn(mockPromise(mockDomainObject));
|
||||
mockStatusCapability.get.andReturn(true);
|
||||
|
||||
action = new CancelAction( mockNavigationService, actionContext);
|
||||
|
||||
});
|
||||
|
||||
it("only applies to domain object with an editor capability", function () {
|
||||
it("only applies to domain object that is being edited", function () {
|
||||
expect(CancelAction.appliesTo(actionContext)).toBeTruthy();
|
||||
expect(mockDomainObject.hasCapability).toHaveBeenCalledWith("editor");
|
||||
|
||||
mockDomainObject.hasCapability.andReturn(false);
|
||||
mockDomainObject.getCapability.andReturn(undefined);
|
||||
mockStatusCapability.get.andReturn(false);
|
||||
expect(CancelAction.appliesTo(actionContext)).toBeFalsy();
|
||||
});
|
||||
|
||||
it("invokes the editor capability's save functionality when performed", function () {
|
||||
it("invokes the editor capability's cancel functionality when" +
|
||||
" performed", function () {
|
||||
// Verify precondition
|
||||
expect(mockEditorCapability.cancel).not.toHaveBeenCalled();
|
||||
action.perform();
|
||||
@ -95,8 +101,8 @@ define(
|
||||
|
||||
it("returns to browse when performed", function () {
|
||||
action.perform();
|
||||
expect(mockLocation.path).toHaveBeenCalledWith(
|
||||
mockUrlService.urlForLocation("browse", mockDomainObject)
|
||||
expect(mockNavigationService.setNavigation).toHaveBeenCalledWith(
|
||||
mockDomainObject
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -19,27 +19,41 @@
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
/*global define,describe,it,expect,beforeEach,jasmine*/
|
||||
/*global define,describe,it,expect,beforeEach,jasmine,spyOn*/
|
||||
|
||||
define(
|
||||
["../../src/actions/EditAction"],
|
||||
[
|
||||
"../../src/actions/EditAction"
|
||||
],
|
||||
function (EditAction) {
|
||||
"use strict";
|
||||
|
||||
describe("The Edit action", function () {
|
||||
var mockLocation,
|
||||
mockNavigationService,
|
||||
var mockNavigationService,
|
||||
mockLog,
|
||||
mockDomainObject,
|
||||
mockStatusCapability,
|
||||
mockType,
|
||||
actionContext,
|
||||
mockCapabilities,
|
||||
mockQ,
|
||||
action;
|
||||
|
||||
function mockPromise(value) {
|
||||
return {
|
||||
then: function (callback) {
|
||||
return mockPromise(callback(value));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
mockLocation = jasmine.createSpyObj(
|
||||
"$location",
|
||||
[ "path" ]
|
||||
|
||||
mockStatusCapability = jasmine.createSpyObj(
|
||||
"statusCapability",
|
||||
[ "get", "set" ]
|
||||
);
|
||||
|
||||
mockNavigationService = jasmine.createSpyObj(
|
||||
"navigationService",
|
||||
[ "setNavigation", "getNavigation" ]
|
||||
@ -50,20 +64,41 @@ define(
|
||||
);
|
||||
mockDomainObject = jasmine.createSpyObj(
|
||||
"domainObject",
|
||||
[ "getId", "getModel", "getCapability" ]
|
||||
[ "getId", "getModel", "getCapability", "hasCapability" ]
|
||||
);
|
||||
mockType = jasmine.createSpyObj(
|
||||
"type",
|
||||
[ "hasFeature" ]
|
||||
);
|
||||
|
||||
mockDomainObject.getCapability.andReturn(mockType);
|
||||
mockQ = jasmine.createSpyObj(
|
||||
"q",
|
||||
[ "when", "all" ]
|
||||
);
|
||||
|
||||
mockQ.when.andReturn(function(value){
|
||||
return mockPromise(value);
|
||||
});
|
||||
|
||||
mockQ.all.andReturn(mockPromise(undefined));
|
||||
|
||||
mockDomainObject.getCapability.andCallFake(function(capability){
|
||||
return mockCapabilities[capability];
|
||||
});
|
||||
mockDomainObject.getModel.andReturn({});
|
||||
mockDomainObject.getId.andReturn("testId");
|
||||
mockStatusCapability.get.andReturn(false);
|
||||
mockType.hasFeature.andReturn(true);
|
||||
|
||||
mockCapabilities = {
|
||||
"status": mockStatusCapability,
|
||||
"type": mockType
|
||||
};
|
||||
|
||||
actionContext = { domainObject: mockDomainObject };
|
||||
|
||||
action = new EditAction(
|
||||
mockLocation,
|
||||
mockQ,
|
||||
mockNavigationService,
|
||||
mockLog,
|
||||
actionContext
|
||||
@ -77,15 +112,30 @@ define(
|
||||
expect(mockType.hasFeature).toHaveBeenCalledWith('creation');
|
||||
});
|
||||
|
||||
it("changes URL path to edit mode when performed", function () {
|
||||
it("is only applicable when domain object is not in edit mode", function () {
|
||||
// Indicates whether object is in edit mode
|
||||
mockStatusCapability.get.andReturn(false);
|
||||
expect(EditAction.appliesTo(actionContext)).toBeTruthy();
|
||||
mockStatusCapability.get.andReturn(true);
|
||||
expect(EditAction.appliesTo(actionContext)).toBeFalsy();
|
||||
});
|
||||
|
||||
it("navigates to editable domain object", function () {
|
||||
spyOn(action, 'createEditableObject');
|
||||
|
||||
action.perform();
|
||||
expect(mockLocation.path).toHaveBeenCalledWith("/edit");
|
||||
|
||||
expect(mockNavigationService.setNavigation).toHaveBeenCalled();
|
||||
expect(action.createEditableObject).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("ensures that the edited object is navigated-to", function () {
|
||||
var navigatedObject;
|
||||
action.perform();
|
||||
expect(mockNavigationService.setNavigation)
|
||||
.toHaveBeenCalledWith(mockDomainObject);
|
||||
navigatedObject = mockNavigationService.setNavigation.mostRecentCall.args[0];
|
||||
expect(navigatedObject.getId())
|
||||
.toEqual(mockDomainObject.getId());
|
||||
expect(navigatedObject).not.toBe(mockDomainObject);
|
||||
});
|
||||
|
||||
it("logs a warning if constructed when inapplicable", function () {
|
||||
@ -94,7 +144,7 @@ define(
|
||||
|
||||
// Should not have hit an exception...
|
||||
new EditAction(
|
||||
mockLocation,
|
||||
mockQ,
|
||||
mockNavigationService,
|
||||
mockLog,
|
||||
{}
|
||||
@ -104,8 +154,6 @@ define(
|
||||
expect(mockLog.warn).toHaveBeenCalled();
|
||||
|
||||
// And should not have had other interactions
|
||||
expect(mockLocation.path)
|
||||
.not.toHaveBeenCalled();
|
||||
expect(mockNavigationService.setNavigation)
|
||||
.not.toHaveBeenCalled();
|
||||
});
|
||||
|
@ -27,10 +27,11 @@ define(
|
||||
"use strict";
|
||||
|
||||
describe("The Save action", function () {
|
||||
var mockLocation,
|
||||
mockDomainObject,
|
||||
var mockDomainObject,
|
||||
mockNavigationService,
|
||||
mockStatusCapability,
|
||||
mockEditorCapability,
|
||||
mockUrlService,
|
||||
mockCapabilities,
|
||||
actionContext,
|
||||
action;
|
||||
|
||||
@ -43,10 +44,6 @@ define(
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
mockLocation = jasmine.createSpyObj(
|
||||
"$location",
|
||||
[ "path" ]
|
||||
);
|
||||
mockDomainObject = jasmine.createSpyObj(
|
||||
"domainObject",
|
||||
[ "getCapability", "hasCapability" ]
|
||||
@ -55,30 +52,39 @@ define(
|
||||
"editor",
|
||||
[ "save", "cancel" ]
|
||||
);
|
||||
mockUrlService = jasmine.createSpyObj(
|
||||
"urlService",
|
||||
["urlForLocation"]
|
||||
mockStatusCapability = jasmine.createSpyObj(
|
||||
"statusCapability",
|
||||
["get"]
|
||||
);
|
||||
|
||||
mockNavigationService = jasmine.createSpyObj(
|
||||
"mockNavigationService",
|
||||
[ "setNavigation"]
|
||||
);
|
||||
mockCapabilities = {
|
||||
"editor": mockEditorCapability,
|
||||
"status": mockStatusCapability
|
||||
};
|
||||
|
||||
actionContext = {
|
||||
domainObject: mockDomainObject
|
||||
};
|
||||
|
||||
mockDomainObject.hasCapability.andReturn(true);
|
||||
mockDomainObject.getCapability.andReturn(mockEditorCapability);
|
||||
mockEditorCapability.save.andReturn(mockPromise(true));
|
||||
mockDomainObject.getCapability.andCallFake(function(capability){
|
||||
return mockCapabilities[capability];
|
||||
});
|
||||
|
||||
action = new SaveAction(mockLocation, mockUrlService, actionContext);
|
||||
mockEditorCapability.save.andReturn(mockPromise(mockDomainObject));
|
||||
mockStatusCapability.get.andReturn(true);
|
||||
|
||||
action = new SaveAction(mockNavigationService, actionContext);
|
||||
|
||||
});
|
||||
|
||||
it("only applies to domain object with an editor capability", function () {
|
||||
it("only applies to domain object that is being edited", function () {
|
||||
expect(SaveAction.appliesTo(actionContext)).toBeTruthy();
|
||||
expect(mockDomainObject.hasCapability).toHaveBeenCalledWith("editor");
|
||||
|
||||
mockDomainObject.hasCapability.andReturn(false);
|
||||
mockDomainObject.getCapability.andReturn(undefined);
|
||||
mockStatusCapability.get.andReturn(false);
|
||||
expect(SaveAction.appliesTo(actionContext)).toBeFalsy();
|
||||
});
|
||||
|
||||
@ -96,8 +102,8 @@ define(
|
||||
|
||||
it("returns to browse when performed", function () {
|
||||
action.perform();
|
||||
expect(mockLocation.path).toHaveBeenCalledWith(
|
||||
mockUrlService.urlForLocation("browse", mockDomainObject)
|
||||
expect(mockNavigationService.setNavigation).toHaveBeenCalledWith(
|
||||
mockDomainObject
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -28,6 +28,8 @@ define(
|
||||
|
||||
describe("The editor capability", function () {
|
||||
var mockPersistence,
|
||||
mockStatusCapability,
|
||||
mockCapabilities,
|
||||
mockEditableObject,
|
||||
mockDomainObject,
|
||||
mockCache,
|
||||
@ -40,6 +42,14 @@ define(
|
||||
"persistence",
|
||||
[ "persist" ]
|
||||
);
|
||||
mockStatusCapability = jasmine.createSpyObj(
|
||||
"status",
|
||||
[ "set" ]
|
||||
);
|
||||
mockCapabilities = {
|
||||
"persistence":mockPersistence,
|
||||
"status": mockStatusCapability
|
||||
};
|
||||
mockEditableObject = {
|
||||
getModel: function () { return model; }
|
||||
};
|
||||
@ -53,7 +63,9 @@ define(
|
||||
);
|
||||
mockCallback = jasmine.createSpy("callback");
|
||||
|
||||
mockDomainObject.getCapability.andReturn(mockPersistence);
|
||||
mockDomainObject.getCapability.andCallFake(function(capability){
|
||||
return mockCapabilities[capability];
|
||||
});
|
||||
|
||||
model = { someKey: "some value", x: 42 };
|
||||
|
||||
@ -96,6 +108,19 @@ define(
|
||||
});
|
||||
});
|
||||
|
||||
it("resets the editing status on successful save", function () {
|
||||
capability.save().then(mockCallback);
|
||||
|
||||
// Wait for promise to resolve
|
||||
waitsFor(function () {
|
||||
return mockCallback.calls.length > 0;
|
||||
}, 250);
|
||||
|
||||
runs(function () {
|
||||
expect(mockStatusCapability.set).toHaveBeenCalledWith('editing', false);
|
||||
});
|
||||
});
|
||||
|
||||
it("has no interactions on cancel", function () {
|
||||
capability.cancel().then(mockCallback);
|
||||
|
||||
@ -111,6 +136,19 @@ define(
|
||||
});
|
||||
});
|
||||
|
||||
it("resets editing status on cancel", function () {
|
||||
capability.cancel().then(mockCallback);
|
||||
|
||||
// Wait for promise to resolve
|
||||
waitsFor(function () {
|
||||
return mockCallback.calls.length > 0;
|
||||
}, 250);
|
||||
|
||||
runs(function () {
|
||||
expect(mockStatusCapability.set).toHaveBeenCalledWith('editing', false);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
}
|
||||
|
@ -32,7 +32,9 @@ define(
|
||||
mockScope,
|
||||
testRepresentation,
|
||||
mockDomainObject,
|
||||
mockStatusCapability,
|
||||
mockPersistence,
|
||||
mockCapabilities,
|
||||
representer;
|
||||
|
||||
function mockPromise(value) {
|
||||
@ -57,11 +59,21 @@ define(
|
||||
]);
|
||||
mockPersistence =
|
||||
jasmine.createSpyObj("persistence", ["persist"]);
|
||||
mockStatusCapability = jasmine.createSpyObj("domainObject", [
|
||||
"get"]);
|
||||
|
||||
mockCapabilities = {
|
||||
"persistence": mockPersistence,
|
||||
"status": mockStatusCapability
|
||||
};
|
||||
|
||||
mockDomainObject.getModel.andReturn({});
|
||||
mockDomainObject.hasCapability.andReturn(true);
|
||||
mockDomainObject.useCapability.andReturn(true);
|
||||
mockDomainObject.getCapability.andReturn(mockPersistence);
|
||||
mockStatusCapability.get.andReturn(true);
|
||||
mockDomainObject.getCapability.andCallFake(function(capability){
|
||||
return mockCapabilities[capability];
|
||||
});
|
||||
|
||||
representer = new EditRepresenter(mockQ, mockLog, mockScope);
|
||||
representer.represent(testRepresentation, mockDomainObject);
|
||||
@ -98,6 +110,14 @@ define(
|
||||
});
|
||||
});
|
||||
|
||||
it("sets an 'editMode' flag on scope if the object is editable", function() {
|
||||
expect(mockScope.editMode).toBe(true);
|
||||
|
||||
mockStatusCapability.get.andReturn(false);
|
||||
representer.represent(testRepresentation, mockDomainObject);
|
||||
expect(mockScope.editMode).toBeFalsy();
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user