Closing or quitting while text is edited applies it first (12.14a)

This commit is contained in:
雷电芽衣 committed 2026-09-30 00:25:34 -04:00
1 parent 5ee5aee07b
commit 94b884a2ef
7 files changed
+247 -2

No files matched your search

+201
View File
@@ -0,0 +1,201 @@
#include "DialogDesk.h"
#include "Document/ProjectWorkspace.h"
#include "UI/ProjectWorkspaceView.h"
#include <QtTest>
// Swift 1.3.1: close or quit mid-text applies the text first.
namespace {
// A saved canvas with `content` typed under the Type tool.
void editText(EditorSession &session, const QString &content)
{
session.createDocument(800, 600, true);
session.history.markSaved();
session.selectTool(NavigationTool::type);
session.beginText(QPointF(100, 100));
TextDraft draft = session.textDraft().value();
draft.style.content = content;
session.setTextDraft(draft);
}
// The callback's answer; empty if it never came.
std::optional<bool> answer(const std::function<void(std::function<void(bool)>)> &call)
{
std::optional<bool> result;
call([&](bool value) { result = value; });
return QTest::qWaitFor([&] { return result.has_value(); }, 10'000) ? result : std::nullopt;
}
QString topText(const EditorSession &session)
{
return session.document().value().layers.back().text.value().style.content;
}
}
class QuitWhileEditingTextTests : public QObject {
Q_OBJECT
private slots:
void closingTheWindowAppliesTheTextThenAsks();
void quittingAppliesTextInQuitOrderAsOneStepEach();
void aRefusedDraftStopsTheQuitWhereItStands();
void textThatCannotBeAppliedHoldsTheQuit();
void editedExistingTextIsOneEditStep();
void unchangedAndBlankDraftsCloseQuietly();
};
void QuitWhileEditingTextTests::closingTheWindowAppliesTheTextThenAsks()
{
ProjectWorkspace workspace;
ProjectWorkspaceView view(workspace);
view.show();
const std::shared_ptr<ProjectTab> tab = workspace.tabs().front();
editText(tab->session, QStringLiteral("Typed"));
DialogDesk desk;
desk.replies = {"Cancel", "Don’t Save"};
// The text is a layer by the time it asks.
desk.note = [&] { return QStringLiteral("draft=%1 text=%2").arg(tab->session.textDraft().has_value()).arg(topText(tab->session)); };
// Cancelled: the window stays, the text a step.
view.close();
QTRY_COMPARE(desk.seen.size(), 1);
QVERIFY2(desk.seen[0].startsWith("alert|2|Save changes to Untitled?") && desk.seen[0].endsWith("|draft=0 text=Typed"), qPrintable(desk.seen[0]));
QTRY_VERIFY(!workspace.isManaging());
QVERIFY(view.isVisible());
QCOMPARE(tab->session.history.undoName(), QString("New Text Layer"));
QVERIFY(tab->session.isModified());
// Asked again, Don't Save closes it.
view.close();
QTRY_VERIFY(!view.isVisible());
QCOMPARE(desk.seen.size(), 2);
}
void QuitWhileEditingTextTests::quittingAppliesTextInQuitOrderAsOneStepEach()
{
ProjectWorkspace workspace;
const std::shared_ptr<ProjectTab> first = workspace.tabs().front();
workspace.newCanvas();
const std::shared_ptr<ProjectTab> second = workspace.tabs().back();
QVERIFY(first != second);
editText(first->session, QStringLiteral("First"));
editText(second->session, QStringLiteral("Second"));
// The tab on screen first; Cancel stops there.
DialogDesk desk;
desk.replies = {"Cancel"};
desk.note = [&] { return workspace.current().session.document().value().layers.back().text.value().style.content; };
QCOMPARE(answer([&](auto done) { workspace.confirmQuit(done); }), std::optional<bool>(false));
QCOMPARE(desk.seen.size(), 1);
QVERIFY2(desk.seen[0].endsWith("|Second"), qPrintable(desk.seen[0]));
for (const auto &[tab, text] : {std::pair(first, QStringLiteral("First")), std::pair(second, QStringLiteral("Second"))}) {
QVERIFY(!tab->session.textDraft());
QCOMPARE(topText(tab->session), text);
QCOMPARE(tab->session.history.undoName(), QString("New Text Layer"));
QVERIFY(tab->session.isModified());
// One step: undo removes the text, redo restores it.
const size_t layers = tab->session.document().value().layers.size();
tab->session.undo();
QCOMPARE(tab->session.document().value().layers.size(), layers - 1);
QVERIFY(!tab->session.isModified());
tab->session.redo();
QCOMPARE(topText(tab->session), text);
}
}
void QuitWhileEditingTextTests::aRefusedDraftStopsTheQuitWhereItStands()
{
ProjectWorkspace workspace;
const std::shared_ptr<ProjectTab> a = workspace.tabs().front();
workspace.newCanvas();
const std::shared_ptr<ProjectTab> b = workspace.tabs().back();
workspace.newCanvas();
const std::shared_ptr<ProjectTab> c = workspace.tabs().back();
// Stored A, B, C; C shown: asked C, A, B.
QCOMPARE(&workspace.current(), c.get());
editText(a->session, QStringLiteral("A"));
editText(b->session, QStringLiteral("B"));
editText(c->session, QStringLiteral("C"));
a->session.setIsProjectBusy(true);
DialogDesk desk;
QCOMPARE(answer([&](auto done) { workspace.confirmQuit(done); }), std::optional<bool>(false));
// C applied; A refused; B never reached; nothing asked.
QVERIFY(!c->session.textDraft());
QCOMPARE(topText(c->session), QString("C"));
QCOMPARE(a->session.textDraft().value().style.content, QString("A"));
QCOMPARE(b->session.textDraft().value().style.content, QString("B"));
QVERIFY(!b->session.isModified());
QVERIFY(desk.seen.isEmpty());
QVERIFY(!workspace.isManaging());
a->session.setIsProjectBusy(false);
}
void QuitWhileEditingTextTests::textThatCannotBeAppliedHoldsTheQuit()
{
ProjectWorkspace workspace;
const std::shared_ptr<ProjectTab> tab = workspace.tabs().front();
editText(tab->session, QStringLiteral("Held"));
// A busy project refuses the text; the quit stops there.
tab->session.setIsProjectBusy(true);
DialogDesk desk;
QCOMPARE(answer([&](auto done) { workspace.confirmQuit(done); }), std::optional<bool>(false));
QVERIFY(desk.seen.isEmpty());
QCOMPARE(tab->session.textDraft().value().style.content, QString("Held"));
QVERIFY(!workspace.isManaging());
tab->session.setIsProjectBusy(false);
}
void QuitWhileEditingTextTests::editedExistingTextIsOneEditStep()
{
ProjectWorkspace workspace;
const std::shared_ptr<ProjectTab> tab = workspace.tabs().front();
EditorSession &session = tab->session;
editText(session, QStringLiteral("Original"));
QVERIFY(session.finishText());
session.history.markSaved();
const QUuid layer = session.document().value().layers.back().id;
const int steps = session.history.undoCount();
// The layer's text opened again and changed.
session.editActiveText();
TextDraft draft = session.textDraft().value();
QCOMPARE(draft.layerID.value(), layer);
draft.style.content = QStringLiteral("Edited");
session.setTextDraft(draft);
DialogDesk desk;
desk.replies = {"Cancel"};
QCOMPARE(answer([&](auto done) { workspace.confirmQuit(done); }), std::optional<bool>(false));
QCOMPARE(desk.seen.size(), 1);
QCOMPARE(session.document().value().layers.back().id, layer);
QCOMPARE(topText(session), QString("Edited"));
QCOMPARE(session.history.undoCount(), steps + 1);
QCOMPARE(session.history.undoName(), QString("Edit Text"));
session.undo();
QCOMPARE(topText(session), QString("Original"));
QVERIFY(!session.isModified());
session.redo();
QCOMPARE(topText(session), QString("Edited"));
}
void QuitWhileEditingTextTests::unchangedAndBlankDraftsCloseQuietly()
{
ProjectWorkspace workspace;
const std::shared_ptr<ProjectTab> first = workspace.tabs().front();
workspace.newCanvas();
const std::shared_ptr<ProjectTab> second = workspace.tabs().back();
// First: saved text opened and left as it was.
editText(first->session, QStringLiteral("Kept"));
QVERIFY(first->session.finishText());
first->session.history.markSaved();
first->session.editActiveText();
QVERIFY(first->session.textDraft().has_value());
// Second: new text with nothing typed.
editText(second->session, QString());
const size_t layers = second->session.document().value().layers.size();
const int firstSteps = first->session.history.undoCount(), secondSteps = second->session.history.undoCount();
DialogDesk desk;
QCOMPARE(answer([&](auto done) { workspace.confirmQuit(done); }), std::optional<bool>(true));
QVERIFY(desk.seen.isEmpty());
QVERIFY(!first->session.textDraft() && !second->session.textDraft());
QCOMPARE(first->session.history.undoCount(), firstSteps);
QCOMPARE(second->session.history.undoCount(), secondSteps);
QCOMPARE(second->session.document().value().layers.size(), layers);
QCOMPARE(topText(first->session), QString("Kept"));
}
QTEST_MAIN(QuitWhileEditingTextTests)
#include "QuitWhileEditingTextTests.moc"