2020-08-01 03:43:15 +02:00
|
|
|
"use strict";
|
|
|
|
|
2020-11-30 23:46:45 +01:00
|
|
|
const {strict: assert} = require("assert");
|
|
|
|
|
2020-07-25 02:02:35 +02:00
|
|
|
const _ = require("lodash");
|
|
|
|
|
2021-09-28 12:20:14 +02:00
|
|
|
const {mock_jquery, mock_esm, set_global, zrequire} = require("../zjsunit/namespace");
|
2020-12-01 00:39:47 +01:00
|
|
|
const {run_test} = require("../zjsunit/test");
|
2021-03-16 23:38:59 +01:00
|
|
|
const blueslip = require("../zjsunit/zblueslip");
|
2021-09-28 12:20:14 +02:00
|
|
|
const {page_params} = require("../zjsunit/zpage_params");
|
2018-08-01 21:17:03 +02:00
|
|
|
|
2020-12-01 23:21:38 +01:00
|
|
|
set_global("setTimeout", (f, delay) => {
|
|
|
|
assert.equal(delay, 0);
|
|
|
|
f();
|
|
|
|
});
|
|
|
|
|
2021-09-28 12:20:14 +02:00
|
|
|
const xhr_401 = {
|
|
|
|
status: 401,
|
2021-10-18 16:30:46 +02:00
|
|
|
responseText: '{"msg": "Use cannot access XYZ"}',
|
2021-09-28 12:20:14 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
let login_to_access_shown = false;
|
|
|
|
mock_esm("../../static/js/spectators", {
|
|
|
|
login_to_access: () => {
|
|
|
|
login_to_access_shown = true;
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
set_global("window", {
|
|
|
|
location: {
|
|
|
|
replace: () => {},
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
2021-03-10 02:39:50 +01:00
|
|
|
const reload_state = zrequire("reload_state");
|
2021-02-10 04:53:22 +01:00
|
|
|
const channel = zrequire("channel");
|
2018-08-01 21:17:03 +02:00
|
|
|
|
2021-10-05 03:01:45 +02:00
|
|
|
const default_stub_xhr = {"default-stub-xhr": 0};
|
2017-06-28 00:48:48 +02:00
|
|
|
|
2021-06-16 15:58:34 +02:00
|
|
|
const $ = mock_jquery({});
|
2021-02-22 12:18:39 +01:00
|
|
|
|
2017-06-28 00:48:48 +02:00
|
|
|
function test_with_mock_ajax(test_params) {
|
2020-07-15 00:34:28 +02:00
|
|
|
const {xhr = default_stub_xhr, run_code, check_ajax_options} = test_params;
|
2018-04-18 22:48:22 +02:00
|
|
|
|
|
|
|
let ajax_called;
|
|
|
|
let ajax_options;
|
2021-02-23 14:37:26 +01:00
|
|
|
$.ajax = (options) => {
|
2017-06-28 00:48:48 +02:00
|
|
|
$.ajax = undefined;
|
|
|
|
ajax_called = true;
|
|
|
|
ajax_options = options;
|
|
|
|
|
2021-02-23 14:37:26 +01:00
|
|
|
options.simulate_success = (data, text_status) => {
|
2017-06-28 00:48:48 +02:00
|
|
|
options.success(data, text_status, xhr);
|
|
|
|
};
|
|
|
|
|
2021-02-23 14:37:26 +01:00
|
|
|
options.simulate_error = () => {
|
2017-06-28 00:48:48 +02:00
|
|
|
options.error(xhr);
|
|
|
|
};
|
|
|
|
|
|
|
|
return xhr;
|
|
|
|
};
|
|
|
|
|
2018-04-18 22:48:22 +02:00
|
|
|
run_code();
|
2021-06-10 08:32:54 +02:00
|
|
|
assert.ok(ajax_called);
|
2018-04-18 22:48:22 +02:00
|
|
|
check_ajax_options(ajax_options);
|
2017-06-28 00:48:48 +02:00
|
|
|
}
|
|
|
|
|
2021-03-20 15:23:44 +01:00
|
|
|
function test(label, f) {
|
2021-06-16 14:38:37 +02:00
|
|
|
run_test(label, ({override}) => {
|
2021-03-20 15:23:44 +01:00
|
|
|
reload_state.clear_for_testing();
|
2021-06-16 14:38:37 +02:00
|
|
|
f({override});
|
2021-03-20 15:23:44 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
test("post", () => {
|
2017-06-28 00:48:48 +02:00
|
|
|
test_with_mock_ajax({
|
2020-07-20 22:18:43 +02:00
|
|
|
run_code() {
|
2017-06-28 00:48:48 +02:00
|
|
|
channel.post({});
|
|
|
|
},
|
|
|
|
|
2020-07-20 22:18:43 +02:00
|
|
|
check_ajax_options(options) {
|
2020-07-15 01:29:15 +02:00
|
|
|
assert.equal(options.type, "POST");
|
|
|
|
assert.equal(options.dataType, "json");
|
2017-06-28 00:48:48 +02:00
|
|
|
|
|
|
|
// Just make sure these don't explode.
|
|
|
|
options.simulate_success();
|
|
|
|
options.simulate_error();
|
|
|
|
},
|
|
|
|
});
|
2021-02-22 12:04:22 +01:00
|
|
|
});
|
2017-06-28 00:48:48 +02:00
|
|
|
|
2021-03-20 15:23:44 +01:00
|
|
|
test("patch", () => {
|
2017-06-28 00:48:48 +02:00
|
|
|
test_with_mock_ajax({
|
2020-07-20 22:18:43 +02:00
|
|
|
run_code() {
|
2017-06-28 00:48:48 +02:00
|
|
|
channel.patch({});
|
|
|
|
},
|
|
|
|
|
2020-07-20 22:18:43 +02:00
|
|
|
check_ajax_options(options) {
|
2020-07-15 01:29:15 +02:00
|
|
|
assert.equal(options.type, "POST");
|
|
|
|
assert.equal(options.data.method, "PATCH");
|
|
|
|
assert.equal(options.dataType, "json");
|
2017-06-28 00:48:48 +02:00
|
|
|
|
|
|
|
// Just make sure these don't explode.
|
|
|
|
options.simulate_success();
|
|
|
|
options.simulate_error();
|
|
|
|
},
|
|
|
|
});
|
2021-02-22 12:04:22 +01:00
|
|
|
});
|
2017-06-28 00:48:48 +02:00
|
|
|
|
2021-03-20 15:23:44 +01:00
|
|
|
test("put", () => {
|
2017-06-28 00:48:48 +02:00
|
|
|
test_with_mock_ajax({
|
2020-07-20 22:18:43 +02:00
|
|
|
run_code() {
|
2017-06-28 00:48:48 +02:00
|
|
|
channel.put({});
|
|
|
|
},
|
|
|
|
|
2020-07-20 22:18:43 +02:00
|
|
|
check_ajax_options(options) {
|
2020-07-15 01:29:15 +02:00
|
|
|
assert.equal(options.type, "PUT");
|
|
|
|
assert.equal(options.dataType, "json");
|
2017-06-28 00:48:48 +02:00
|
|
|
|
|
|
|
// Just make sure these don't explode.
|
|
|
|
options.simulate_success();
|
|
|
|
options.simulate_error();
|
|
|
|
},
|
|
|
|
});
|
2021-02-22 12:04:22 +01:00
|
|
|
});
|
2017-06-28 00:48:48 +02:00
|
|
|
|
2021-03-20 15:23:44 +01:00
|
|
|
test("delete", () => {
|
2017-06-28 00:48:48 +02:00
|
|
|
test_with_mock_ajax({
|
2020-07-20 22:18:43 +02:00
|
|
|
run_code() {
|
2017-06-28 00:48:48 +02:00
|
|
|
channel.del({});
|
|
|
|
},
|
|
|
|
|
2020-07-20 22:18:43 +02:00
|
|
|
check_ajax_options(options) {
|
2020-07-15 01:29:15 +02:00
|
|
|
assert.equal(options.type, "DELETE");
|
|
|
|
assert.equal(options.dataType, "json");
|
2017-06-28 00:48:48 +02:00
|
|
|
|
|
|
|
// Just make sure these don't explode.
|
|
|
|
options.simulate_success();
|
|
|
|
options.simulate_error();
|
|
|
|
},
|
|
|
|
});
|
2021-02-22 12:04:22 +01:00
|
|
|
});
|
2017-06-28 00:48:48 +02:00
|
|
|
|
2021-03-20 15:23:44 +01:00
|
|
|
test("get", () => {
|
2017-06-28 00:48:48 +02:00
|
|
|
test_with_mock_ajax({
|
2020-07-20 22:18:43 +02:00
|
|
|
run_code() {
|
2017-06-28 00:48:48 +02:00
|
|
|
channel.get({});
|
|
|
|
},
|
|
|
|
|
2020-07-20 22:18:43 +02:00
|
|
|
check_ajax_options(options) {
|
2020-07-15 01:29:15 +02:00
|
|
|
assert.equal(options.type, "GET");
|
|
|
|
assert.equal(options.dataType, "json");
|
2017-06-28 00:48:48 +02:00
|
|
|
|
|
|
|
// Just make sure these don't explode.
|
|
|
|
options.simulate_success();
|
|
|
|
options.simulate_error();
|
|
|
|
},
|
|
|
|
});
|
2018-05-15 12:40:07 +02:00
|
|
|
});
|
2017-06-28 00:48:48 +02:00
|
|
|
|
2021-03-20 15:23:44 +01:00
|
|
|
test("normal_post", () => {
|
2018-04-18 22:48:22 +02:00
|
|
|
const data = {
|
2020-07-15 01:29:15 +02:00
|
|
|
s: "some_string",
|
2017-06-28 00:48:48 +02:00
|
|
|
num: 7,
|
|
|
|
lst: [1, 2, 4, 8],
|
|
|
|
};
|
|
|
|
|
2018-04-18 22:48:22 +02:00
|
|
|
let orig_success_called;
|
|
|
|
let orig_error_called;
|
2021-10-05 03:01:45 +02:00
|
|
|
const stub_xhr = {"stub-xhr-normal-post": 0};
|
2017-06-28 00:48:48 +02:00
|
|
|
|
|
|
|
test_with_mock_ajax({
|
|
|
|
xhr: stub_xhr,
|
|
|
|
|
2020-07-20 22:18:43 +02:00
|
|
|
run_code() {
|
2017-06-28 00:48:48 +02:00
|
|
|
channel.post({
|
2020-07-20 22:18:43 +02:00
|
|
|
data,
|
2020-07-15 01:29:15 +02:00
|
|
|
url: "/json/endpoint",
|
2020-07-20 22:18:43 +02:00
|
|
|
success(data, text_status, xhr) {
|
2017-06-28 00:48:48 +02:00
|
|
|
orig_success_called = true;
|
2020-07-15 01:29:15 +02:00
|
|
|
assert.equal(data, "response data");
|
|
|
|
assert.equal(text_status, "success");
|
2017-06-28 00:48:48 +02:00
|
|
|
assert.equal(xhr, stub_xhr);
|
|
|
|
},
|
2020-07-20 22:18:43 +02:00
|
|
|
error() {
|
2017-06-28 00:48:48 +02:00
|
|
|
orig_error_called = true;
|
|
|
|
},
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2020-07-20 22:18:43 +02:00
|
|
|
check_ajax_options(options) {
|
2020-07-15 01:29:15 +02:00
|
|
|
assert.equal(options.type, "POST");
|
|
|
|
assert.equal(options.dataType, "json");
|
2017-06-28 00:48:48 +02:00
|
|
|
assert.deepEqual(options.data, data);
|
2020-07-15 01:29:15 +02:00
|
|
|
assert.equal(options.url, "/json/endpoint");
|
2017-06-28 00:48:48 +02:00
|
|
|
|
2020-07-15 01:29:15 +02:00
|
|
|
options.simulate_success("response data", "success");
|
2021-06-10 08:32:54 +02:00
|
|
|
assert.ok(orig_success_called);
|
2017-06-28 00:48:48 +02:00
|
|
|
|
|
|
|
options.simulate_error();
|
2021-06-10 08:32:54 +02:00
|
|
|
assert.ok(orig_error_called);
|
2017-06-28 00:48:48 +02:00
|
|
|
},
|
|
|
|
});
|
2018-05-15 12:40:07 +02:00
|
|
|
});
|
2017-06-28 00:48:48 +02:00
|
|
|
|
2021-03-20 15:23:44 +01:00
|
|
|
test("patch_with_form_data", () => {
|
2018-04-18 22:48:22 +02:00
|
|
|
let appended;
|
2017-06-28 00:48:48 +02:00
|
|
|
|
2018-04-18 22:48:22 +02:00
|
|
|
const data = {
|
2020-07-20 22:18:43 +02:00
|
|
|
append(k, v) {
|
2020-07-15 01:29:15 +02:00
|
|
|
assert.equal(k, "method");
|
|
|
|
assert.equal(v, "PATCH");
|
2017-06-28 00:48:48 +02:00
|
|
|
appended = true;
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
test_with_mock_ajax({
|
2020-07-20 22:18:43 +02:00
|
|
|
run_code() {
|
2017-06-28 00:48:48 +02:00
|
|
|
channel.patch({
|
2020-07-20 22:18:43 +02:00
|
|
|
data,
|
2017-06-28 00:48:48 +02:00
|
|
|
processData: false,
|
|
|
|
});
|
2021-06-10 08:32:54 +02:00
|
|
|
assert.ok(appended);
|
2017-06-28 00:48:48 +02:00
|
|
|
},
|
|
|
|
|
2020-07-20 22:18:43 +02:00
|
|
|
check_ajax_options(options) {
|
2020-07-15 01:29:15 +02:00
|
|
|
assert.equal(options.type, "POST");
|
|
|
|
assert.equal(options.dataType, "json");
|
2017-06-28 00:48:48 +02:00
|
|
|
|
|
|
|
// Just make sure these don't explode.
|
|
|
|
options.simulate_success();
|
|
|
|
options.simulate_error();
|
|
|
|
},
|
|
|
|
});
|
2018-05-15 12:40:07 +02:00
|
|
|
});
|
2017-06-28 00:48:48 +02:00
|
|
|
|
2021-09-28 12:20:14 +02:00
|
|
|
test("authentication_error_401_is_spectator", () => {
|
|
|
|
test_with_mock_ajax({
|
|
|
|
xhr: xhr_401,
|
|
|
|
run_code() {
|
|
|
|
channel.post({});
|
|
|
|
},
|
|
|
|
|
|
|
|
// is_spectator = true
|
|
|
|
check_ajax_options(options) {
|
|
|
|
page_params.is_spectator = true;
|
|
|
|
|
|
|
|
options.simulate_error();
|
|
|
|
assert.ok(login_to_access_shown);
|
|
|
|
|
|
|
|
login_to_access_shown = false;
|
|
|
|
},
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
test("authentication_error_401_password_change_in_progress", () => {
|
|
|
|
test_with_mock_ajax({
|
|
|
|
xhr: xhr_401,
|
|
|
|
run_code() {
|
|
|
|
channel.post({});
|
|
|
|
},
|
|
|
|
|
|
|
|
// is_spectator = true
|
|
|
|
// password_change_in_progress = true
|
|
|
|
check_ajax_options(options) {
|
|
|
|
page_params.is_spectator = true;
|
2021-10-05 03:01:45 +02:00
|
|
|
channel.set_password_change_in_progress(true);
|
2021-09-28 12:20:14 +02:00
|
|
|
|
|
|
|
options.simulate_error();
|
|
|
|
assert.ok(!login_to_access_shown);
|
|
|
|
|
2021-10-05 03:01:45 +02:00
|
|
|
channel.set_password_change_in_progress(false);
|
2021-09-28 12:20:14 +02:00
|
|
|
page_params.is_spectator = false;
|
|
|
|
login_to_access_shown = false;
|
|
|
|
},
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
test("authentication_error_401_not_spectator", () => {
|
|
|
|
test_with_mock_ajax({
|
|
|
|
xhr: xhr_401,
|
|
|
|
run_code() {
|
|
|
|
channel.post({});
|
|
|
|
},
|
|
|
|
|
|
|
|
// is_spectator = false
|
|
|
|
check_ajax_options(options) {
|
|
|
|
page_params.is_spectator = false;
|
|
|
|
|
|
|
|
options.simulate_error();
|
|
|
|
assert.ok(!login_to_access_shown);
|
|
|
|
|
|
|
|
login_to_access_shown = false;
|
|
|
|
},
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2021-03-20 15:23:44 +01:00
|
|
|
test("reload_on_403_error", () => {
|
2017-06-28 00:48:48 +02:00
|
|
|
test_with_mock_ajax({
|
|
|
|
xhr: {
|
|
|
|
status: 403,
|
2017-07-25 03:30:13 +02:00
|
|
|
responseText: '{"msg": "CSRF Fehler: etwas", "code": "CSRF_FAILED"}',
|
2017-06-28 00:48:48 +02:00
|
|
|
},
|
|
|
|
|
2020-07-20 22:18:43 +02:00
|
|
|
run_code() {
|
2017-06-28 00:48:48 +02:00
|
|
|
channel.post({});
|
|
|
|
},
|
|
|
|
|
2020-07-20 22:18:43 +02:00
|
|
|
check_ajax_options(options) {
|
2021-03-10 02:39:50 +01:00
|
|
|
let handler_called = false;
|
|
|
|
reload_state.set_csrf_failed_handler(() => {
|
|
|
|
handler_called = true;
|
|
|
|
});
|
2017-06-28 00:48:48 +02:00
|
|
|
|
|
|
|
options.simulate_error();
|
2021-06-10 08:32:54 +02:00
|
|
|
assert.ok(handler_called);
|
2017-06-28 00:48:48 +02:00
|
|
|
},
|
|
|
|
});
|
2018-05-15 12:40:07 +02:00
|
|
|
});
|
2017-06-28 00:48:48 +02:00
|
|
|
|
2021-03-20 15:23:44 +01:00
|
|
|
test("unexpected_403_response", () => {
|
2017-06-28 00:48:48 +02:00
|
|
|
test_with_mock_ajax({
|
|
|
|
xhr: {
|
|
|
|
status: 403,
|
2020-07-15 01:29:15 +02:00
|
|
|
responseText: "unexpected",
|
2017-06-28 00:48:48 +02:00
|
|
|
},
|
|
|
|
|
2020-07-20 22:18:43 +02:00
|
|
|
run_code() {
|
2017-06-28 00:48:48 +02:00
|
|
|
channel.post({});
|
|
|
|
},
|
|
|
|
|
2020-07-20 22:18:43 +02:00
|
|
|
check_ajax_options(options) {
|
2020-07-15 01:29:15 +02:00
|
|
|
blueslip.expect("error", "Unexpected 403 response from server");
|
2017-06-28 00:48:48 +02:00
|
|
|
options.simulate_error();
|
|
|
|
},
|
|
|
|
});
|
2018-05-15 12:40:07 +02:00
|
|
|
});
|
2017-06-28 00:48:48 +02:00
|
|
|
|
2021-03-20 15:23:44 +01:00
|
|
|
test("retry", () => {
|
2017-06-28 00:48:48 +02:00
|
|
|
test_with_mock_ajax({
|
2020-07-20 22:18:43 +02:00
|
|
|
run_code() {
|
2017-06-28 00:48:48 +02:00
|
|
|
channel.post({
|
|
|
|
idempotent: true,
|
|
|
|
data: 42,
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2020-07-20 22:18:43 +02:00
|
|
|
check_ajax_options(options) {
|
2020-07-15 01:29:15 +02:00
|
|
|
blueslip.expect("log", "Retrying idempotent[object Object]");
|
2017-06-28 00:48:48 +02:00
|
|
|
test_with_mock_ajax({
|
2020-07-20 22:18:43 +02:00
|
|
|
run_code() {
|
2017-06-28 00:48:48 +02:00
|
|
|
options.simulate_success();
|
|
|
|
},
|
|
|
|
|
2020-07-20 22:18:43 +02:00
|
|
|
check_ajax_options(options) {
|
2017-06-28 00:48:48 +02:00
|
|
|
assert.equal(options.data, 42);
|
|
|
|
},
|
|
|
|
});
|
|
|
|
},
|
|
|
|
});
|
2018-05-15 12:40:07 +02:00
|
|
|
});
|
2017-06-28 00:48:48 +02:00
|
|
|
|
2021-03-20 15:23:44 +01:00
|
|
|
test("too_many_pending", () => {
|
2021-03-08 15:14:43 +01:00
|
|
|
channel.clear_for_tests();
|
2021-02-23 14:37:26 +01:00
|
|
|
$.ajax = () => {
|
2021-10-05 03:01:45 +02:00
|
|
|
const xhr = {stub: 0};
|
2017-06-28 00:48:48 +02:00
|
|
|
return xhr;
|
|
|
|
};
|
|
|
|
|
2020-07-15 00:34:28 +02:00
|
|
|
blueslip.expect(
|
|
|
|
"warn",
|
|
|
|
"The length of pending_requests is over 50. " +
|
|
|
|
"Most likely they are not being correctly removed.",
|
|
|
|
);
|
2021-03-08 15:14:43 +01:00
|
|
|
_.times(51, () => {
|
2017-06-28 00:48:48 +02:00
|
|
|
channel.post({});
|
|
|
|
});
|
2021-03-08 15:14:43 +01:00
|
|
|
channel.clear_for_tests();
|
2018-05-15 12:40:07 +02:00
|
|
|
});
|
2017-06-28 00:48:48 +02:00
|
|
|
|
2021-03-20 15:23:44 +01:00
|
|
|
test("xhr_error_message", () => {
|
2018-04-18 22:48:22 +02:00
|
|
|
let xhr = {
|
2020-07-15 01:29:15 +02:00
|
|
|
status: "200",
|
|
|
|
responseText: "does not matter",
|
2017-06-28 00:48:48 +02:00
|
|
|
};
|
2020-07-15 01:29:15 +02:00
|
|
|
let msg = "data added";
|
|
|
|
assert.equal(channel.xhr_error_message(msg, xhr), "data added");
|
2017-06-28 00:48:48 +02:00
|
|
|
|
|
|
|
xhr = {
|
2020-07-15 01:29:15 +02:00
|
|
|
status: "404",
|
2017-06-28 00:48:48 +02:00
|
|
|
responseText: '{"msg": "file not found"}',
|
|
|
|
};
|
2020-07-15 01:29:15 +02:00
|
|
|
msg = "some message";
|
|
|
|
assert.equal(channel.xhr_error_message(msg, xhr), "some message: file not found");
|
2018-05-15 12:40:07 +02:00
|
|
|
});
|
2021-03-18 12:56:06 +01:00
|
|
|
|
2021-03-20 15:23:44 +01:00
|
|
|
test("while_reloading", () => {
|
2021-03-18 12:56:06 +01:00
|
|
|
reload_state.set_state_to_in_progress();
|
|
|
|
|
|
|
|
assert.equal(channel.get({ignore_reload: false}), undefined);
|
|
|
|
|
|
|
|
let orig_success_called = false;
|
|
|
|
let orig_error_called = false;
|
|
|
|
|
|
|
|
test_with_mock_ajax({
|
|
|
|
run_code() {
|
|
|
|
channel.del({
|
|
|
|
url: "/json/endpoint",
|
|
|
|
ignore_reload: true,
|
|
|
|
success() {
|
|
|
|
orig_success_called = true;
|
|
|
|
},
|
|
|
|
error() {
|
|
|
|
orig_error_called = true;
|
|
|
|
},
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
check_ajax_options(options) {
|
|
|
|
blueslip.expect("log", "Ignoring DELETE /json/endpoint response while reloading");
|
|
|
|
options.simulate_success();
|
2021-06-10 08:32:54 +02:00
|
|
|
assert.ok(!orig_success_called);
|
2021-03-18 12:56:06 +01:00
|
|
|
|
|
|
|
blueslip.expect("log", "Ignoring DELETE /json/endpoint error response while reloading");
|
|
|
|
options.simulate_error();
|
2021-06-10 08:32:54 +02:00
|
|
|
assert.ok(!orig_error_called);
|
2021-03-18 12:56:06 +01:00
|
|
|
},
|
|
|
|
});
|
|
|
|
});
|