added test script NewScore10InstrPutNotesSaveClose

This commit is contained in:
Igor Korsukov 2021-10-07 17:49:06 +02:00
parent 285e70eb38
commit 80d1778740
22 changed files with 290 additions and 33 deletions

View file

@ -82,6 +82,8 @@ option(ENGRAVING_COMPAT_WRITESTYLE_302 "Write style to score xml file" ON)
option(ENGRAVING_COMPAT_WRITEEXCERPTS_302 "Write excerpts to score xml file" ON)
# -----
option(UI_DISABLE_MODALITY "Disable dialogs modality for testing purpose" OFF)
option(ACCESSIBILITY_LOGGING_ENABLED "Enable accessibility logging" OFF)
option(SOUNDFONT3 "Ogg Vorbis compressed fonts" ON) # Enable Ogg Vorbis compressed fonts, requires Ogg & Vorbis

View file

@ -72,6 +72,8 @@
#cmakedefine ENGRAVING_COMPAT_WRITESTYLE_302
#cmakedefine ENGRAVING_COMPAT_WRITEEXCERPTS_302
#cmakedefine UI_DISABLE_MODALITY
#cmakedefine ACCESSIBILITY_LOGGING_ENABLED
#cmakedefine SPARKLE_ENABLED

View file

@ -330,8 +330,8 @@ MenuItem AppMenuModel::diagnosticItem() const
};
MenuItemList autobotItems {
makeMenuItem("autobot-show-batchtests"),
makeMenuItem("autobot-show-scripts"),
makeMenuItem("autobot-show-batchtests"),
};
MenuItemList items {

View file

@ -36,7 +36,8 @@ public:
virtual io::path dataPath() const = 0;
virtual io::path filesPath() const = 0;
virtual io::path testingFilesPath() const = 0;
virtual io::path savingFilesPath() const = 0;
virtual io::path drawDataPath() const = 0;
virtual io::path fileDrawDataPath(const io::path& filePath) const = 0;

View file

@ -22,6 +22,9 @@
#include "autobotapi.h"
#include <QTimer>
#include <QFileInfo>
#include <QDir>
#include <QDateTime>
#include "async/async.h"
@ -36,12 +39,23 @@ AutobotApi::AutobotApi(IApiEngine* e)
bool AutobotApi::openProject(const QString& name)
{
io::path dir = autobotConfiguration()->filesPath();
io::path dir = autobotConfiguration()->testingFilesPath();
io::path filePath = dir + "/" + name;
Ret ret = projectFilesController()->openProject(filePath);
return ret;
}
void AutobotApi::saveProject(const QString& name)
{
io::path dir = autobotConfiguration()->savingFilesPath();
if (!QFileInfo::exists(dir.toQString())) {
QDir().mkpath(dir.toQString());
}
io::path filePath = dir + "/" + QDateTime::currentDateTime().toString(Qt::ISODate) + "_" + name;
projectFilesController()->saveProject(filePath);
}
void AutobotApi::setInterval(int msec)
{
m_intervalMsec = msec;
@ -109,7 +123,7 @@ bool AutobotApi::pause()
return true;
}
void AutobotApi::sleep(int msec)
void AutobotApi::sleep(int msec) const
{
if (msec < 0) {
msec = m_intervalMsec;
@ -122,12 +136,17 @@ void AutobotApi::sleep(int msec)
loop.exec();
}
void AutobotApi::waitPopup()
void AutobotApi::waitPopup() const
{
//! NOTE We could do it smartly, check a current popup actually opened, but or just sleep some time
sleep(500);
}
void AutobotApi::seeChanges(int msec)
{
sleep(msec);
}
void AutobotApi::async(const QJSValue& func, const QJSValueList& args)
{
async::Async::call(this, [func, args]() {
@ -135,3 +154,10 @@ void AutobotApi::async(const QJSValue& func, const QJSValueList& args)
mut_func.call(args);
});
}
int AutobotApi::randomInt(int min, int max) const
{
srand(time(nullptr)); // Seed the time
int val = rand() % (max - min + 1) + min;
return val;
}

View file

@ -49,12 +49,15 @@ public:
Q_INVOKABLE void runTestCase(const QJSValue& testCase);
Q_INVOKABLE bool openProject(const QString& name);
Q_INVOKABLE void saveProject(const QString& name = QString());
Q_INVOKABLE void abort();
Q_INVOKABLE bool pause();
Q_INVOKABLE void sleep(int msec = -1);
Q_INVOKABLE void waitPopup();
Q_INVOKABLE void sleep(int msec = -1) const;
Q_INVOKABLE void waitPopup() const;
Q_INVOKABLE void seeChanges(int msec = 300);
Q_INVOKABLE void async(const QJSValue& func, const QJSValueList& args = QJSValueList());
Q_INVOKABLE int randomInt(int min, int max) const;
private:

View file

@ -24,10 +24,19 @@
#include "log.h"
using namespace mu::api;
using namespace mu::ui;
NavigationApi::NavigationApi(IApiEngine* e)
: ApiObject(e)
{
//! NOTE Disable reset on mouse press for testing purpose
navigation()->setIsResetOnMousePress(false);
}
NavigationApi::~NavigationApi()
{
//! NOTE Return default
navigation()->setIsResetOnMousePress(true);
}
void NavigationApi::nextPanel()
@ -60,6 +69,11 @@ void NavigationApi::down()
dispatcher()->dispatch("nav-down");
}
void NavigationApi::escape()
{
dispatcher()->dispatch("nav-escape");
}
bool NavigationApi::goToControl(const QString& section, const QString& panel, const QString& contol)
{
bool ok = navigation()->requestActivateByName(section.toStdString(), panel.toStdString(), contol.toStdString());
@ -79,3 +93,21 @@ bool NavigationApi::triggerControl(const QString& section, const QString& panel,
}
return ok;
}
QString NavigationApi::activeSection() const
{
INavigationSection* sec = navigation()->activeSection();
return sec ? sec->name() : QString();
}
QString NavigationApi::activePanel() const
{
INavigationPanel* p = navigation()->activePanel();
return p ? p->name() : QString();
}
QString NavigationApi::activeControl() const
{
INavigationControl* c = navigation()->activeControl();
return c ? c->name() : QString();
}

View file

@ -39,6 +39,7 @@ class NavigationApi : public ApiObject
public:
explicit NavigationApi(IApiEngine* e);
~NavigationApi();
Q_INVOKABLE void nextPanel();
Q_INVOKABLE void prevPanel();
@ -46,9 +47,14 @@ public:
Q_INVOKABLE void left();
Q_INVOKABLE void up();
Q_INVOKABLE void down();
Q_INVOKABLE void escape();
Q_INVOKABLE bool goToControl(const QString& section, const QString& panel, const QString& contol);
Q_INVOKABLE void trigger();
Q_INVOKABLE bool triggerControl(const QString& section, const QString& panel, const QString& contol);
Q_INVOKABLE QString activeSection() const;
Q_INVOKABLE QString activePanel() const;
Q_INVOKABLE QString activeControl() const;
};
}

View file

@ -113,7 +113,7 @@ mu::RetVal<mu::io::paths> Autobot::filesList() const
{
using namespace mu::system;
io::path filesPath = configuration()->filesPath();
io::path filesPath = configuration()->testingFilesPath();
LOGI() << "filesPath: " << filesPath;
RetVal<io::paths> paths = fileSystem()->scanFiles(filesPath, QStringList(), IFileSystem::ScanMode::OnlyCurrentDir);
return paths;

View file

@ -22,24 +22,36 @@
#include "autobotconfiguration.h"
#include <cstdlib>
#include <QDir>
using namespace mu::autobot;
bool AutobotConfiguration::isConfigured() const
{
return !dataPath().empty() && !filesPath().empty();
return !dataPath().empty() && !testingFilesPath().empty();
}
mu::io::path AutobotConfiguration::dataPath() const
{
return io::path(std::getenv("MU_AUTOBOT_DATA_PATH"));
io::path p = io::path(std::getenv("MU_AUTOBOT_DATA_PATH"));
if (p.empty()) {
QDir projDir(QString(PROJECT_ROOT_DIR));
projDir.cdUp();
p = projDir.absolutePath() + "/mu_autobot_data";
}
return p;
}
mu::io::path AutobotConfiguration::filesPath() const
mu::io::path AutobotConfiguration::testingFilesPath() const
{
return io::path(std::getenv("MU_AUTOBOT_FILES_PATH"));
}
mu::io::path AutobotConfiguration::savingFilesPath() const
{
return dataPath() + "/saving_files";
}
mu::io::path AutobotConfiguration::drawDataPath() const
{
return dataPath() + "/draw_data";

View file

@ -34,7 +34,8 @@ public:
io::path dataPath() const override;
io::path filesPath() const override;
io::path testingFilesPath() const override;
io::path savingFilesPath() const override;
io::path drawDataPath() const override;
io::path fileDrawDataPath(const io::path& filePath) const override;

View file

@ -28,8 +28,8 @@ StyledDialogView {
title: "Autobot"
contentHeight: 800
contentWidth: 600
contentHeight: 600
contentWidth: 400
resizable: true
ScriptsPanel {

View file

@ -46,6 +46,8 @@ public:
virtual INavigationControl* activeControl() const = 0;
virtual async::Notification navigationChanged() const = 0;
virtual void setIsResetOnMousePress(bool arg) = 0;
};
}

View file

@ -315,13 +315,30 @@ const std::set<INavigationSection*>& NavigationController::sections() const
return m_sections;
}
void NavigationController::setIsResetOnMousePress(bool arg)
{
m_isResetOnMousePress = arg;
}
void NavigationController::resetActiveIfNeed(QObject* watched)
{
if (!m_isResetOnMousePress) {
return;
}
#ifdef BUILD_DIAGNOSTICS
if (diagnostics::isDiagnosticHierarchy(watched)) {
return;
}
#endif
resetActive();
}
bool NavigationController::eventFilter(QObject* watched, QEvent* event)
{
if (event->type() == QEvent::MouseButtonPress) {
#ifdef BUILD_DIAGNOSTICS
if (!diagnostics::isDiagnosticHierarchy(watched))
#endif
resetActive();
resetActiveIfNeed(watched);
}
return QObject::eventFilter(watched, event);
@ -420,7 +437,12 @@ void NavigationController::doActivatePanel(INavigationPanel* panel)
ctrlIndex.row = idxVal.at(0).toInt();
ctrlIndex.column = idxVal.at(1).toInt();
control = findByIndex(panel->controls(), ctrlIndex);
IF_ASSERT_FAILED(control) {
if (!control) {
bool isOptional = event->data.value("controlOptional").toBool();
if (!isOptional) {
IF_ASSERT_FAILED(control) {
}
}
}
}
}

View file

@ -63,6 +63,8 @@ public:
async::Notification navigationChanged() const override;
void setIsResetOnMousePress(bool arg) override;
void init();
private:
@ -100,10 +102,12 @@ private:
void doActivateFirst();
void doActivateLast();
void resetActiveIfNeed(QObject* watched);
void resetActive();
std::set<INavigationSection*> m_sections;
async::Notification m_navigationChanged;
bool m_isResetOnMousePress = true;
};
}

View file

@ -36,6 +36,7 @@
#include "popupwindow/popupwindow_qquickview.h"
#include "log.h"
#include "config.h"
using namespace mu::uicomponents;
@ -161,6 +162,9 @@ void PopupView::open()
}
qWindow->setTitle(m_title);
qWindow->setModality(m_modal ? Qt::ApplicationModal : Qt::NonModal);
#ifdef UI_DISABLE_MODALITY
qWindow->setModality(Qt::NonModal);
#endif
QRect winRect = m_window->geometry();
qWindow->setMinimumSize(winRect.size());

View file

@ -58,6 +58,8 @@ Item {
onNavigationEvent: {
if (event.type === NavigationEvent.AboutActive) {
event.setData("controlIndex", prv.currentItemNavigationIndex)
//! NOTE If we changed family, then control with saved index maybe not
event.setData("controlOptional", true)
}
}
}

View file

@ -42,9 +42,9 @@ void ProjectFilesController::init()
dispatcher()->reg(this, "file-new", this, &ProjectFilesController::newProject);
dispatcher()->reg(this, "file-close", [this]() { closeOpenedProject(); });
dispatcher()->reg(this, "file-save", this, &ProjectFilesController::saveScore);
dispatcher()->reg(this, "file-save-as", this, &ProjectFilesController::saveScoreAs);
dispatcher()->reg(this, "file-save-a-copy", this, &ProjectFilesController::saveScoreCopy);
dispatcher()->reg(this, "file-save", [this]() { saveProject(); });
dispatcher()->reg(this, "file-save-as", this, &ProjectFilesController::saveProjectAs);
dispatcher()->reg(this, "file-save-a-copy", this, &ProjectFilesController::saveProjectCopy);
dispatcher()->reg(this, "file-save-selection", this, &ProjectFilesController::saveSelection);
dispatcher()->reg(this, "file-save-online", this, &ProjectFilesController::saveOnline);
@ -206,7 +206,7 @@ bool ProjectFilesController::closeOpenedProject()
if (btn == IInteractive::Button::Cancel) {
return false;
} else if (btn == IInteractive::Button::Save) {
saveScore();
saveProject();
}
}
@ -238,16 +238,21 @@ IInteractive::Button ProjectFilesController::askAboutSavingScore(const io::path&
return result.standardButton();
}
void ProjectFilesController::saveScore()
void ProjectFilesController::saveProject(const io::path& path)
{
if (!currentNotationProject()->created().val) {
doSaveScore();
return;
}
io::path defaultFilePath = defaultSavingFilePath();
io::path filePath;
if (!path.empty()) {
filePath = path;
} else {
io::path defaultFilePath = defaultSavingFilePath();
filePath = selectScoreSavingFile(defaultFilePath, qtrc("project", "Save score"));
}
io::path filePath = selectScoreSavingFile(defaultFilePath, qtrc("project", "Save score"));
if (filePath.empty()) {
return;
}
@ -259,7 +264,7 @@ void ProjectFilesController::saveScore()
doSaveScore(filePath);
}
void ProjectFilesController::saveScoreAs()
void ProjectFilesController::saveProjectAs()
{
io::path defaultFilePath = defaultSavingFilePath();
io::path selectedFilePath = selectScoreSavingFile(defaultFilePath, qtrc("project", "Save score"));
@ -270,7 +275,7 @@ void ProjectFilesController::saveScoreAs()
doSaveScore(selectedFilePath, SaveMode::SaveAs);
}
void ProjectFilesController::saveScoreCopy()
void ProjectFilesController::saveProjectCopy()
{
io::path defaultFilePath = defaultSavingFilePath();
io::path selectedFilePath = selectScoreSavingFile(defaultFilePath, qtrc("project", "Save a copy"));

View file

@ -56,6 +56,7 @@ public:
Ret openProject(const io::path& projectPath) override;
bool closeOpenedProject() override;
bool isProjectOpened(const io::path& scorePath) const override;
void saveProject(const io::path& path = io::path()) override;
private:
void setupConnections();
@ -72,9 +73,8 @@ private:
bool checkCanIgnoreError(const Ret& ret, const io::path& filePath);
framework::IInteractive::Button askAboutSavingScore(const io::path& filePath);
void saveScore();
void saveScoreAs();
void saveScoreCopy();
void saveProjectAs();
void saveProjectCopy();
void saveSelection();
void saveOnline();

View file

@ -34,9 +34,10 @@ class IProjectFilesController : MODULE_EXPORT_INTERFACE
public:
virtual ~IProjectFilesController() = default;
virtual Ret openProject(const io::path& scorePath) = 0;
virtual Ret openProject(const io::path& path) = 0;
virtual bool closeOpenedProject() = 0;
virtual bool isProjectOpened(const io::path& scorePath) const = 0;
virtual bool isProjectOpened(const io::path& path) const = 0;
virtual void saveProject(const io::path& path = io::path()) = 0;
};
}

View file

@ -0,0 +1,49 @@
var NewScore = require("steps/NewScore.js")
function main()
{
var testCase = {
name: "NewScore10InstrPutNotesSaveClose",
steps: [
{name: "Open Dialog", func: function() {
NewScore.openNewScoreDialog()
}},
{name: "Select Instruments", func: function() {
NewScore.chooseRandomInstruments(10)
}},
{name: "Note input mode", func: function() {
api.navigation.triggerControl("NoteInputSection", "NoteInputBar", "note-input-steptime")
api.autobot.waitPopup()
// First item become automatically current, so just trigger
api.navigation.trigger()
// Select note
api.navigation.triggerControl("NoteInputSection", "NoteInputBar", "pad-note-8")
}},
{name: "Note input", func: function() {
api.dispatcher.dispatch("note-c")
api.dispatcher.dispatch("note-d")
api.dispatcher.dispatch("note-e")
api.dispatcher.dispatch("note-f")
api.dispatcher.dispatch("note-g")
api.dispatcher.dispatch("note-a")
api.dispatcher.dispatch("note-b")
}},
{name: "Save", func: function() {
api.autobot.saveProject("NewScore10InstrPutNotesSaveClose.mscz")
}},
{name: "Close", func: function() {
api.dispatcher.dispatch("file-close")
api.autobot.seeChanges()
// Go Home
api.navigation.triggerControl("TopTool", "MainToolBar", "Home")
}}
]
};
api.autobot.setInterval(1000)
api.autobot.runTestCase(testCase)
api.log.info("----------- end script ---------------")
}

View file

@ -0,0 +1,83 @@
function openNewScoreDialog()
{
api.navigation.triggerControl("AppTitleBar", "AppMenuBar", "&File")
// wait popup open
api.autobot.waitPopup()
// New become automatically current, so just trigger
api.navigation.trigger()
}
function сhooseFluteAndPiano()
{
// Flute
api.navigation.goToControl("NewScoreDialog", "FamilyView", "Woodwinds")
api.navigation.goToControl("NewScoreDialog", "InstrumentsView", "Flute")
api.navigation.triggerControl("NewScoreDialog", "SelectPanel", "Select")
// just for see changes
api.autobot.sleep(500)
// Piano
api.navigation.goToControl("NewScoreDialog", "FamilyView", "Keyboards")
api.navigation.goToControl("NewScoreDialog", "InstrumentsView", "Piano")
api.navigation.triggerControl("NewScoreDialog", "SelectPanel", "Select")
// just for see changes
api.autobot.sleep(500)
// Done
api.navigation.triggerControl("NewScoreDialog", "BottomPanel", "Done")
}
function chooseRandomInstruments(count, see_msec)
{
see_msec = see_msec || 50
api.log.debug("chooseRandomInstruments count: " + count)
for (var i = 0; i < count; i++) {
api.log.debug("chooseRandomInstruments i: " + i)
// Go to first family
api.navigation.goToControl("NewScoreDialog", "FamilyView", "Woodwinds")
// Choose family
var familyCount = api.autobot.randomInt(0, 20);
api.log.debug("chooseRandomInstruments familyCount: " + familyCount)
for (var f = 0; f < familyCount; f++) {
api.navigation.down()
api.autobot.seeChanges(see_msec)
}
if (api.navigation.activeControl() === "genreBox") {
api.navigation.down()
}
// Got to Instruments
api.navigation.nextPanel()
api.autobot.seeChanges(see_msec)
// Choose instrument
var instrCount = api.autobot.randomInt(0, 20);
api.log.debug("chooseRandomInstruments instrCount: " + instrCount)
for (var j = 0; j < instrCount; j++) {
api.navigation.down()
api.autobot.seeChanges(see_msec)
}
if (api.navigation.activeControl() === "SearchInstruments") {
api.navigation.down()
}
// Select
api.navigation.triggerControl("NewScoreDialog", "SelectPanel", "Select")
}
// Done
api.navigation.triggerControl("NewScoreDialog", "BottomPanel", "Done")
}
module.exports = {
openNewScoreDialog: openNewScoreDialog,
сhooseFluteAndPiano: сhooseFluteAndPiano,
chooseRandomInstruments: chooseRandomInstruments
}