Bug 1875495 - Port bug 1780071 - Linter should yell if using assignment or comparisons inside assertion conditions. r=aleca
Didn't spot any direct errors, but these are the rewrites the plugin does for `--fix` Differential Revision: https://phabricator.services.mozilla.com/D199105 --HG-- extra : amend_source : aa962db6dbcba906fb65b8a74ce1c0ad4f367d9f
This commit is contained in:
Родитель
6941b2cf07
Коммит
2cbcc82537
|
@ -372,8 +372,9 @@ async function doModifyItemTest(aCalendar) {
|
|||
alarmObserver.expectResult(aCalendar, item, alarm, EXPECT_TIMER);
|
||||
alarmObserver.checkExpected("doModifyItemTest Test 5, floating timezone");
|
||||
const newTimer = alarmObserver.getTimer(aCalendar.id, item.hashId, alarm.icalString);
|
||||
ok(
|
||||
newTimer.delay - oldTimer.delay <= 1000,
|
||||
Assert.lessOrEqual(
|
||||
newTimer.delay - oldTimer.delay,
|
||||
1000,
|
||||
"doModifyItemTest Test 5, floating timezone; check timer value"
|
||||
);
|
||||
}
|
||||
|
@ -423,8 +424,9 @@ async function doAcknowledgeTest(aCalendar) {
|
|||
|
||||
// the snoozed alarm timer delay should be close to an hour
|
||||
const tmr = alarmObserver.getTimer(aCalendar.id, item.hashId, alarm.icalString);
|
||||
ok(
|
||||
Math.abs(tmr.delay - 3600000) <= 1000,
|
||||
Assert.lessOrEqual(
|
||||
Math.abs(tmr.delay - 3600000),
|
||||
1000,
|
||||
"doAcknowledgeTest, snoozed alarm timer delay close to an hour"
|
||||
);
|
||||
|
||||
|
|
|
@ -222,8 +222,9 @@ function test_doubleParameters() {
|
|||
"Value " + parValues[parIndex] + " for parameter " + parNames[parIndex]
|
||||
);
|
||||
}
|
||||
ok(
|
||||
parNames.length == aExpected[att_n].param.length,
|
||||
Assert.equal(
|
||||
parNames.length,
|
||||
aExpected[att_n].param.length,
|
||||
"Each parameter has been considered for " + att_n
|
||||
);
|
||||
}
|
||||
|
|
|
@ -76,6 +76,6 @@ function serializeEvent_test() {
|
|||
serializer.addItems([event]);
|
||||
const serialized = ics_unfoldline(serializer.serializeToString());
|
||||
for (const id of expectedIds) {
|
||||
ok(serialized.search(id) != -1);
|
||||
Assert.notEqual(serialized.search(id), -1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -94,6 +94,6 @@ function check_absolute(aIcalString) {
|
|||
const alarm = new CalAlarm();
|
||||
alarm.icalString = aIcalString;
|
||||
equal(alarm.related, Ci.calIAlarm.ALARM_RELATED_ABSOLUTE);
|
||||
ok(alarm.alarmDate != null);
|
||||
Assert.notEqual(alarm.alarmDate, null);
|
||||
equal(alarm.offset, null);
|
||||
}
|
||||
|
|
|
@ -407,7 +407,7 @@ add_test(function test_calprefs() {
|
|||
memory = cal.manager.createCalendar("memory", Services.io.newURI("moz-memory-calendar://"));
|
||||
memory.id = memid;
|
||||
prop = memory.getProperty("intpref");
|
||||
ok(prop === null);
|
||||
Assert.strictEqual(prop, null);
|
||||
|
||||
// We are done now, start the next test
|
||||
run_next_test();
|
||||
|
|
|
@ -24,8 +24,8 @@ function run_test() {
|
|||
const dateTime2050 = cal.createDateTime();
|
||||
dateTime2050.year = 2050;
|
||||
|
||||
ok(dateTime1950.nativeTime < dateTime1955.nativeTime);
|
||||
ok(dateTime1955.nativeTime < dateTime1965.nativeTime);
|
||||
ok(dateTime1965.nativeTime < dateTime1990.nativeTime);
|
||||
ok(dateTime1990.nativeTime < dateTime2050.nativeTime);
|
||||
Assert.less(dateTime1950.nativeTime, dateTime1955.nativeTime);
|
||||
Assert.less(dateTime1955.nativeTime, dateTime1965.nativeTime);
|
||||
Assert.less(dateTime1965.nativeTime, dateTime1990.nativeTime);
|
||||
Assert.less(dateTime1990.nativeTime, dateTime2050.nativeTime);
|
||||
}
|
||||
|
|
|
@ -683,8 +683,9 @@ add_task(async function createInvitationOverlay_test() {
|
|||
let i;
|
||||
for (i = 0; i < test.expected.attendeesList.length; i++) {
|
||||
const { name, title, icon } = test.expected.attendeesList[i];
|
||||
ok(
|
||||
attendeeNodes.length > i,
|
||||
Assert.greater(
|
||||
attendeeNodes.length,
|
||||
i,
|
||||
`Enough attendees for expected attendee #${i} ${name} (test ${test.name})`
|
||||
);
|
||||
assertAttendee(attendeeNodes[i], name, title, icon, test.name);
|
||||
|
@ -989,14 +990,30 @@ add_task(async function updateInvitationOverlay_test() {
|
|||
ok(node.classList.contains("removed"), `Text "${text}" is removed (test ${testName})`);
|
||||
break;
|
||||
case "modified":
|
||||
ok(node.tagName !== "DEL", `Text "${text}" is not deleted (test ${testName})`);
|
||||
ok(node.tagName !== "INS", `Text "${text}" is not inserted (test ${testName})`);
|
||||
Assert.notStrictEqual(
|
||||
node.tagName,
|
||||
"DEL",
|
||||
`Text "${text}" is not deleted (test ${testName})`
|
||||
);
|
||||
Assert.notStrictEqual(
|
||||
node.tagName,
|
||||
"INS",
|
||||
`Text "${text}" is not inserted (test ${testName})`
|
||||
);
|
||||
ok(node.classList.contains("modified"), `Text "${text}" is modified (test ${testName})`);
|
||||
break;
|
||||
case "same":
|
||||
// NOTE: node may be a Text node.
|
||||
ok(node.tagName !== "DEL", `Text "${text}" is not deleted (test ${testName})`);
|
||||
ok(node.tagName !== "INS", `Text "${text}" is not inserted (test ${testName})`);
|
||||
Assert.notStrictEqual(
|
||||
node.tagName,
|
||||
"DEL",
|
||||
`Text "${text}" is not deleted (test ${testName})`
|
||||
);
|
||||
Assert.notStrictEqual(
|
||||
node.tagName,
|
||||
"INS",
|
||||
`Text "${text}" is not inserted (test ${testName})`
|
||||
);
|
||||
if (node.classList) {
|
||||
ok(!node.classList.contains("added"), `Text "${text}" is not added (test ${testName})`);
|
||||
ok(
|
||||
|
@ -1055,8 +1072,9 @@ add_task(async function updateInvitationOverlay_test() {
|
|||
if (first) {
|
||||
first = false;
|
||||
} else if (insertBreaks) {
|
||||
ok(
|
||||
nodeList.length > nodeIndex,
|
||||
Assert.greater(
|
||||
nodeList.length,
|
||||
nodeIndex,
|
||||
`Enough child nodes for expected break node at index ${nodeIndex} (test ${test.name})`
|
||||
);
|
||||
equal(
|
||||
|
@ -1067,8 +1085,9 @@ add_task(async function updateInvitationOverlay_test() {
|
|||
nodeIndex++;
|
||||
}
|
||||
|
||||
ok(
|
||||
nodeList.length > nodeIndex,
|
||||
Assert.greater(
|
||||
nodeList.length,
|
||||
nodeIndex,
|
||||
`Enough child nodes for expected node at index ${nodeIndex} "${text}" (test ${test.name})`
|
||||
);
|
||||
assertElement(nodeList[nodeIndex], text, type, test.name);
|
||||
|
@ -1643,12 +1662,14 @@ add_task(async function parseCounter_test() {
|
|||
missingProps.push(prop);
|
||||
}
|
||||
}
|
||||
ok(
|
||||
additionalProps.length == 0,
|
||||
Assert.equal(
|
||||
additionalProps.length,
|
||||
0,
|
||||
`(test ${test.name}: should be no additional properties: ${additionalProps})`
|
||||
);
|
||||
ok(
|
||||
missingProps.length == 0,
|
||||
Assert.equal(
|
||||
missingProps.length,
|
||||
0,
|
||||
`(test ${test.name}: should be no missing properties: ${missingProps})`
|
||||
);
|
||||
}
|
||||
|
|
|
@ -311,8 +311,8 @@ add_task(async function testMetaData() {
|
|||
values = aCalendar.getAllMetaDataValues();
|
||||
equal(values.length, 1);
|
||||
equal(ids.length, 1);
|
||||
ok(ids[0] == "item2");
|
||||
ok(values[0] == "meta2");
|
||||
Assert.equal(ids[0], "item2");
|
||||
Assert.equal(values[0], "meta2");
|
||||
|
||||
aCalendar.deleteMetaData("item2");
|
||||
equal(aCalendar.getMetaData("item2"), null);
|
||||
|
|
|
@ -959,23 +959,23 @@ function test_interface() {
|
|||
occ1.QueryInterface(Ci.calIEvent);
|
||||
occ1.startDate = cal.createDateTime("20020401T114500");
|
||||
rinfo.modifyException(occ1, true);
|
||||
ok(rinfo.getExceptionFor(occDate1) != null);
|
||||
Assert.notEqual(rinfo.getExceptionFor(occDate1), null);
|
||||
|
||||
// modifyException immutable
|
||||
const occ2 = rinfo.getOccurrenceFor(occDate2);
|
||||
occ2.makeImmutable();
|
||||
rinfo.modifyException(occ2, true);
|
||||
ok(rinfo.getExceptionFor(occDate2) != null);
|
||||
Assert.notEqual(rinfo.getExceptionFor(occDate2), null);
|
||||
|
||||
// getExceptionIds
|
||||
const ids = rinfo.getExceptionIds();
|
||||
equal(ids.length, 2);
|
||||
ok(ids[0].compare(occDate1) == 0);
|
||||
ok(ids[1].compare(occDate2) == 0);
|
||||
Assert.equal(ids[0].compare(occDate1), 0);
|
||||
Assert.equal(ids[1].compare(occDate2), 0);
|
||||
|
||||
// removeExceptionFor
|
||||
rinfo.removeExceptionFor(occDate1);
|
||||
ok(rinfo.getExceptionFor(occDate1) == null);
|
||||
Assert.equal(rinfo.getExceptionFor(occDate1), null);
|
||||
equal(rinfo.getExceptionIds().length, 1);
|
||||
}
|
||||
|
||||
|
|
|
@ -359,7 +359,7 @@ add_task(async function countOccurrences_test() {
|
|||
parser.parseString(ics);
|
||||
const items = parser.getItems();
|
||||
|
||||
ok(items.length > 0, "parsing input succeeded (test #" + i + ")");
|
||||
Assert.greater(items.length, 0, "parsing input succeeded (test #" + i + ")");
|
||||
for (const item of items) {
|
||||
equal(
|
||||
countOccurrences(item),
|
||||
|
|
|
@ -538,7 +538,7 @@ var test_logFileSplitting = async function () {
|
|||
logWriter._messageCount = messageCountLimit;
|
||||
await logMessage(message);
|
||||
notEqual(oldPath, logWriter.currentPath);
|
||||
ok(logWriter._startTime > oldStartTime);
|
||||
Assert.greater(logWriter._startTime, oldStartTime);
|
||||
|
||||
// Do it again with the same message.
|
||||
oldStartTime = logWriter._startTime;
|
||||
|
@ -546,7 +546,7 @@ var test_logFileSplitting = async function () {
|
|||
logWriter._messageCount = messageCountLimit;
|
||||
await logMessage(message);
|
||||
notEqual(oldPath, logWriter.currentPath);
|
||||
ok(logWriter._startTime > oldStartTime);
|
||||
Assert.greater(logWriter._startTime, oldStartTime);
|
||||
|
||||
// Clean up.
|
||||
await IOUtils.remove(logDirPath, { recursive: true });
|
||||
|
|
|
@ -12,7 +12,7 @@ add_task(async () => {
|
|||
}, "chrome-document-loaded");
|
||||
openContentTab(TEST_DOCUMENT_URL);
|
||||
});
|
||||
ok(testDocument.URL == TEST_DOCUMENT_URL);
|
||||
Assert.equal(testDocument.URL, TEST_DOCUMENT_URL);
|
||||
const testWindow = testDocument.ownerGlobal;
|
||||
const MENULIST_CLASS = testWindow.customElements.get("menulist");
|
||||
const MENULIST_EDITABLE_CLASS =
|
||||
|
|
|
@ -140,7 +140,11 @@ add_task(async function test_sideloading() {
|
|||
addons.children[0].click();
|
||||
|
||||
// The click should hide the main menu. This is currently synchronous.
|
||||
ok(PanelUI.panel.state != "open", "Main menu is closed or closing.");
|
||||
Assert.notEqual(
|
||||
PanelUI.panel.state,
|
||||
"open",
|
||||
"Main menu is closed or closing."
|
||||
);
|
||||
|
||||
let panel = await popupPromise;
|
||||
|
||||
|
|
|
@ -108,7 +108,11 @@ async function backgroundUpdateTest(url, id, checkIconFn) {
|
|||
addons.children[0].click();
|
||||
|
||||
// The click should hide the main menu. This is currently synchronous.
|
||||
ok(PanelUI.panel.state != "open", "Main menu is closed or closing.");
|
||||
Assert.notEqual(
|
||||
PanelUI.panel.state,
|
||||
"open",
|
||||
"Main menu is closed or closing."
|
||||
);
|
||||
|
||||
// Wait for the permission prompt, check the contents
|
||||
let panel = await popupPromise;
|
||||
|
|
|
@ -610,7 +610,7 @@ add_task(async function () {
|
|||
|
||||
registerCleanupFunction(() => {
|
||||
// The appmenu should be closed by the end of the test.
|
||||
ok(PanelUI.panel.state == "closed", "Main menu is closed.");
|
||||
Assert.equal(PanelUI.panel.state, "closed", "Main menu is closed.");
|
||||
|
||||
// Any opened tabs should be closed by the end of the test.
|
||||
const tabmail = document.getElementById("tabmail");
|
||||
|
|
|
@ -179,7 +179,7 @@ add_task(async () => {
|
|||
});
|
||||
info("attachment added");
|
||||
await promiseAnimationFrame(composeWindow);
|
||||
ok(toolbarButton.open === false);
|
||||
Assert.strictEqual(toolbarButton.open, false);
|
||||
|
||||
is(bucket.itemCount, 1);
|
||||
const attachment = bucket.itemChildren[0];
|
||||
|
|
|
@ -300,7 +300,7 @@ add_task(async function test_user_defined_commands() {
|
|||
const keysetID = `ext-keyset-id-${makeWidgetId(extension.id)}`;
|
||||
|
||||
let keyset = win1.document.getElementById(keysetID);
|
||||
ok(keyset != null, "Expected keyset to exist");
|
||||
Assert.notEqual(keyset, null, "Expected keyset to exist");
|
||||
is(
|
||||
keyset.children.length,
|
||||
expectedCommandsRegistered,
|
||||
|
@ -308,7 +308,7 @@ add_task(async function test_user_defined_commands() {
|
|||
);
|
||||
|
||||
keyset = win2.document.getElementById(keysetID);
|
||||
ok(keyset != null, "Expected keyset to exist");
|
||||
Assert.notEqual(keyset, null, "Expected keyset to exist");
|
||||
is(
|
||||
keyset.children.length,
|
||||
expectedCommandsRegistered,
|
||||
|
@ -316,7 +316,7 @@ add_task(async function test_user_defined_commands() {
|
|||
);
|
||||
|
||||
keyset = win3.document.getElementById(keysetID);
|
||||
ok(keyset != null, "Expected keyset to exist");
|
||||
Assert.notEqual(keyset, null, "Expected keyset to exist");
|
||||
is(
|
||||
keyset.children.length,
|
||||
expectedCommandsRegistered,
|
||||
|
@ -548,7 +548,7 @@ add_task(async function test_commands_MV3_event_page() {
|
|||
];
|
||||
for (const i in windows) {
|
||||
const keyset = windows[i].window.document.getElementById(keysetID);
|
||||
ok(keyset != null, "Expected keyset to exist");
|
||||
Assert.notEqual(keyset, null, "Expected keyset to exist");
|
||||
is(
|
||||
keyset.children.length,
|
||||
expectedCommandsRegistered,
|
||||
|
|
|
@ -416,7 +416,7 @@ add_task(async function testChangeDetails() {
|
|||
await messagesInOutbox(2);
|
||||
|
||||
const outboxMessages = [...outbox.messages];
|
||||
ok(outboxMessages.length > 0);
|
||||
Assert.greater(outboxMessages.length, 0);
|
||||
const sentMessage5 = outboxMessages.shift();
|
||||
is(sentMessage5.author, "nondefault@invalid", "author was changed");
|
||||
is(sentMessage5.subject, "Changed by listener5", "subject was changed");
|
||||
|
@ -434,7 +434,7 @@ add_task(async function testChangeDetails() {
|
|||
});
|
||||
});
|
||||
|
||||
ok(outboxMessages.length > 0);
|
||||
Assert.greater(outboxMessages.length, 0);
|
||||
const sentMessage6 = outboxMessages.shift();
|
||||
is(sentMessage6.author, "nondefault@invalid", "author was changed");
|
||||
is(sentMessage6.subject, "Changed by listener6", "subject was changed");
|
||||
|
@ -452,7 +452,7 @@ add_task(async function testChangeDetails() {
|
|||
});
|
||||
});
|
||||
|
||||
ok(outboxMessages.length == 0);
|
||||
Assert.equal(outboxMessages.length, 0);
|
||||
|
||||
await new Promise(resolve => {
|
||||
outbox.deleteMessages(
|
||||
|
@ -545,7 +545,7 @@ add_task(async function testChangeAttachments() {
|
|||
await messagesInOutbox(1);
|
||||
|
||||
const outboxMessages = [...outbox.messages];
|
||||
ok(outboxMessages.length > 0);
|
||||
Assert.greater(outboxMessages.length, 0);
|
||||
const sentMessage12 = outboxMessages.shift();
|
||||
|
||||
await new Promise(resolve => {
|
||||
|
@ -558,7 +558,7 @@ add_task(async function testChangeAttachments() {
|
|||
});
|
||||
});
|
||||
|
||||
ok(outboxMessages.length == 0);
|
||||
Assert.equal(outboxMessages.length, 0);
|
||||
|
||||
await new Promise(resolve => {
|
||||
outbox.deleteMessages(
|
||||
|
@ -724,7 +724,7 @@ add_task(async function testListExpansion() {
|
|||
await messagesInOutbox(2);
|
||||
|
||||
const outboxMessages = [...outbox.messages];
|
||||
ok(outboxMessages.length > 0);
|
||||
Assert.greater(outboxMessages.length, 0);
|
||||
const sentMessage7 = outboxMessages.shift();
|
||||
is(sentMessage7.subject, "Changed by listener7", "subject was changed");
|
||||
is(
|
||||
|
@ -738,7 +738,7 @@ add_task(async function testListExpansion() {
|
|||
"list in changed field was expanded"
|
||||
);
|
||||
|
||||
ok(outboxMessages.length > 0);
|
||||
Assert.greater(outboxMessages.length, 0);
|
||||
const sentMessage8 = outboxMessages.shift();
|
||||
is(sentMessage8.subject, "Test", "subject was not changed");
|
||||
is(
|
||||
|
@ -747,7 +747,7 @@ add_task(async function testListExpansion() {
|
|||
"list in unchanged field was expanded"
|
||||
);
|
||||
|
||||
ok(outboxMessages.length == 0);
|
||||
Assert.equal(outboxMessages.length, 0);
|
||||
|
||||
await new Promise(resolve => {
|
||||
outbox.deleteMessages(
|
||||
|
|
|
@ -67,17 +67,17 @@ add_task(async function () {
|
|||
|
||||
const dummyUID = "9b9074ff-8fa4-4c58-9c3b-bc9ea2e17db1";
|
||||
let searchBook = MailServices.ab.getDirectoryFromUID(dummyUID);
|
||||
ok(searchBook == null, "Dummy directory was removed by extension");
|
||||
Assert.equal(searchBook, null, "Dummy directory was removed by extension");
|
||||
|
||||
const UID = "00e1d9af-a846-4ef5-a6ac-15e8926bf6d3";
|
||||
searchBook = MailServices.ab.getDirectoryFromUID(UID);
|
||||
ok(searchBook != null, "Extension registered an async directory");
|
||||
Assert.notEqual(searchBook, null, "Extension registered an async directory");
|
||||
|
||||
let foundCards = 0;
|
||||
await new Promise(resolve => {
|
||||
searchBook.search(null, "test", {
|
||||
onSearchFoundCard(card) {
|
||||
ok(card != null, "A card was found.");
|
||||
Assert.notEqual(card, null, "A card was found.");
|
||||
equal(card.directoryUID, UID, "The card comes from the directory.");
|
||||
equal(
|
||||
card.primaryEmail,
|
||||
|
@ -128,7 +128,7 @@ add_task(async function () {
|
|||
|
||||
await extension.unload();
|
||||
searchBook = MailServices.ab.getDirectoryFromUID(UID);
|
||||
ok(searchBook == null, "Extension directory removed after unload");
|
||||
Assert.equal(searchBook, null, "Extension directory removed after unload");
|
||||
});
|
||||
|
||||
registerCleanupFunction(() => {
|
||||
|
|
|
@ -54,7 +54,7 @@ add_task(async function testCollapse() {
|
|||
const hiddenGroup = messageParent.querySelector(".hide-children");
|
||||
const toggle = hiddenGroup.querySelector(".eventToggle");
|
||||
ok(toggle);
|
||||
ok(hiddenGroup.querySelectorAll(".event-row").length >= 5);
|
||||
Assert.greaterOrEqual(hiddenGroup.querySelectorAll(".event-row").length, 5);
|
||||
|
||||
toggle.click();
|
||||
await BrowserTestUtils.waitForMutationCondition(
|
||||
|
|
|
@ -723,7 +723,7 @@ add_task(async function accountListOverflow() {
|
|||
}
|
||||
} while (++count < 25);
|
||||
|
||||
ok(count < 24); // If count reaches 25, we have a problem.
|
||||
Assert.less(count, 24); // If count reaches 25, we have a problem.
|
||||
ok(!menuButton.hidden);
|
||||
|
||||
// Remove the added accounts. The list of buttons should not reappear and the
|
||||
|
|
|
@ -167,8 +167,9 @@ async function testCheckboxes(paneID, scrollPaneTo, ...tests) {
|
|||
}
|
||||
for (const selector of test.enabledElements) {
|
||||
const elements = prefsDocument.querySelectorAll(selector);
|
||||
ok(
|
||||
elements.length >= 1,
|
||||
Assert.greaterOrEqual(
|
||||
elements.length,
|
||||
1,
|
||||
`At least one element matched '${selector}'`
|
||||
);
|
||||
for (const element of elements) {
|
||||
|
@ -256,8 +257,9 @@ async function testRadioButtons(paneID, scrollPaneTo, ...tests) {
|
|||
if (state.enabledElements) {
|
||||
for (const selector of state.enabledElements) {
|
||||
const elements = prefsDocument.querySelectorAll(selector);
|
||||
ok(
|
||||
elements.length >= 1,
|
||||
Assert.greaterOrEqual(
|
||||
elements.length,
|
||||
1,
|
||||
`At least one element matched '${selector}'`
|
||||
);
|
||||
for (const element of elements) {
|
||||
|
|
|
@ -93,5 +93,5 @@ add_task(async function testProfileExport() {
|
|||
|
||||
const exportZipStat = await IOUtils.stat(zipFile);
|
||||
info(exportZipStat.size);
|
||||
ok(exportZipStat.size > 10, "Zip is not empty");
|
||||
Assert.greater(exportZipStat.size, 10, "Zip is not empty");
|
||||
});
|
||||
|
|
|
@ -138,7 +138,10 @@ function run_test() {
|
|||
|
||||
// 'gServer1' should be deferred. Get the path of the root folder to which
|
||||
// other accounts are deferred.
|
||||
ok(gServer1.rootFolder.filePath.path != gServer1.rootMsgFolder.filePath.path);
|
||||
Assert.notEqual(
|
||||
gServer1.rootFolder.filePath.path,
|
||||
gServer1.rootMsgFolder.filePath.path
|
||||
);
|
||||
const deferredToRootFolder = gServer1.rootMsgFolder.filePath.path;
|
||||
|
||||
// Account to which other accounts have been deferred.
|
||||
|
|
Загрузка…
Ссылка в новой задаче