/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { class JsProvTest : public UnoApiTest { public: JsProvTest() : UnoApiTest(u"/scripting/qa/extras"_ustr) { } void setUp() override; void tearDown() override; private: css::uno::Reference m_xFileAccess; css::uno::Reference m_xUriHelper; css::uno::Reference m_xScriptProvider; css::uno::Reference createScriptProvider(); void createDummyMacro(const OUString& sDirectoryUri); void testEditableCreatable(); void testCreate(); void testRun(); void testUnrelatedFile(); void testFolder(); void testCopyable(); CPPUNIT_TEST_SUITE(JsProvTest); CPPUNIT_TEST(testEditableCreatable); CPPUNIT_TEST(testCreate); CPPUNIT_TEST(testRun); CPPUNIT_TEST(testUnrelatedFile); CPPUNIT_TEST(testFolder); CPPUNIT_TEST(testCopyable); CPPUNIT_TEST_SUITE_END(); }; void JsProvTest::setUp() { UnoApiTest::setUp(); m_xFileAccess = css::ucb::SimpleFileAccess::create(m_xContext); // The ExpandContentProvider is needed for ScriptURIHelper to work css::uno::Reference xUcb = css::ucb::UniversalContentBroker::create(m_xContext); css::uno::Reference xExpandProvider( m_xSFactory->createInstance(u"com.sun.star.ucb.ExpandContentProvider"_ustr), css::uno::UNO_QUERY_THROW); xUcb->registerContentProvider(xExpandProvider, u"vnd.sun.star.expand"_ustr, true); m_xUriHelper = css::script::provider::ScriptURIHelper::create(getComponentContext(), u"JavaScript"_ustr, u"user"_ustr); m_xScriptProvider = createScriptProvider(); } void JsProvTest::tearDown() { m_xScriptProvider.clear(); // Delete the JavaScript scripting directory so that each test can have a clean slate if (m_xFileAccess->isFolder(m_xUriHelper->getRootStorageURI())) m_xFileAccess->kill(m_xUriHelper->getRootStorageURI()); m_xFileAccess.clear(); m_xUriHelper.clear(); UnoApiTest::tearDown(); } css::uno::Reference JsProvTest::createScriptProvider() { // Create the script provider by searching for a factory with the right implementation in case // there are multiple JavaScript providers css::uno::Reference xEnumAccess( m_xFactory, css::uno::UNO_QUERY_THROW); css::uno::Reference xEnum = xEnumAccess->createContentEnumeration( "com.sun.star.script.provider.ScriptProviderForJavaScript"); while (xEnum->hasMoreElements()) { css::uno::Reference xFactory(xEnum->nextElement(), css::uno::UNO_QUERY_THROW); css::uno::Reference xServiceInfo(xFactory, css::uno::UNO_QUERY_THROW); if (xServiceInfo->getImplementationName() != "com.sun.star.singleprov.ScriptProviderForJavaScript") continue; css::uno::Sequence aArgs(1); aArgs.getArray()[0] <<= u"user"_ustr; css::uno::Reference xProvider( xFactory->createInstanceWithArgumentsAndContext(aArgs, m_xContext), css::uno::UNO_QUERY_THROW); return xProvider; } CPPUNIT_FAIL("Couldn’t find com.sun.star.singleprov.ScriptProviderForJavaScript " "implementation"); } bool isEditable(const css::uno::Reference& xNode) { css::uno::Reference xEditable(xNode, css::uno::UNO_QUERY); return xEditable.is() && xEditable->isEditableNode(); } bool isCreatable(const css::uno::Reference& xNode) { css::uno::Reference xCreatable(xNode, css::uno::UNO_QUERY); return xCreatable.is() && xCreatable->isCreatableNode(); } void JsProvTest::testEditableCreatable() { // Create a dummy macro in the script directory createDummyMacro(m_xUriHelper->getRootStorageURI() + "/MyScript.js"); // The root node should be creatable but not editable css::uno::Reference xRootNode(m_xScriptProvider, css::uno::UNO_QUERY_THROW); CPPUNIT_ASSERT(isCreatable(xRootNode)); CPPUNIT_ASSERT(!isEditable(xRootNode)); css::uno::Reference xBrowseNode(m_xScriptProvider, css::uno::UNO_QUERY_THROW); // The root node should be a container CPPUNIT_ASSERT_EQUAL(css::script::browse::BrowseNodeTypes::CONTAINER, xBrowseNode->getType()); CPPUNIT_ASSERT_EQUAL(u"JavaScript"_ustr, xBrowseNode->getName()); // The root node should have exactly one child which is the script we created above CPPUNIT_ASSERT(xBrowseNode->hasChildNodes()); CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xBrowseNode->getChildNodes().getLength()); xBrowseNode = xBrowseNode->getChildNodes()[0]; // The next level down should be a container node to represent the module CPPUNIT_ASSERT_EQUAL(css::script::browse::BrowseNodeTypes::CONTAINER, xBrowseNode->getType()); CPPUNIT_ASSERT_EQUAL(u"MyScript"_ustr, xBrowseNode->getName()); // The module should be editable but not creatable CPPUNIT_ASSERT(!isCreatable(xBrowseNode)); CPPUNIT_ASSERT(isEditable(xBrowseNode)); // The module should have exactly one child to represent the macro CPPUNIT_ASSERT(xBrowseNode->hasChildNodes()); CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xBrowseNode->getChildNodes().getLength()); xBrowseNode = xBrowseNode->getChildNodes()[0]; // Finally we should be on a node representing the actual macro CPPUNIT_ASSERT_EQUAL(css::script::browse::BrowseNodeTypes::SCRIPT, xBrowseNode->getType()); CPPUNIT_ASSERT_EQUAL(u"MyScript"_ustr, xBrowseNode->getName()); // The macro should be editable but not creatable CPPUNIT_ASSERT(!isCreatable(xBrowseNode)); CPPUNIT_ASSERT(isEditable(xBrowseNode)); // The macro should have no children CPPUNIT_ASSERT(!xBrowseNode->hasChildNodes()); CPPUNIT_ASSERT_EQUAL(sal_Int32(0), xBrowseNode->getChildNodes().getLength()); // Test the macro’s URL css::uno::Reference xPropertySet(xBrowseNode, css::uno::UNO_QUERY_THROW); OUString sMacroUri; bool bConvertResult = xPropertySet->getPropertyValue("URI") >>= sMacroUri; CPPUNIT_ASSERT(bConvertResult); CPPUNIT_ASSERT_EQUAL(u"vnd.sun.star.script:MyScript.js?language=JavaScript&location=user"_ustr, sMacroUri); } void JsProvTest::testCreate() { css::uno::Reference xBrowseNode(m_xScriptProvider, css::uno::UNO_QUERY_THROW); // The root node should start by having no children CPPUNIT_ASSERT_EQUAL(sal_Int32(0), xBrowseNode->getChildNodes().getLength()); // The root node should be creatable CPPUNIT_ASSERT(isCreatable(xBrowseNode)); // Invoke the create method css::uno::Reference xCreatable( xBrowseNode, css::uno::UNO_QUERY_THROW); xCreatable->createNode(u"My Script"_ustr); // That should have created the file CPPUNIT_ASSERT(m_xFileAccess->exists(m_xUriHelper->getRootStorageURI() + "/My Script.js")); // The file should now appear as a child of the root node CPPUNIT_ASSERT(xBrowseNode->hasChildNodes()); CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xBrowseNode->getChildNodes().getLength()); CPPUNIT_ASSERT_EQUAL(u"My Script"_ustr, xBrowseNode->getChildNodes()[0]->getName()); } void JsProvTest::createDummyMacro(const OUString& sDirectoryUri) { // Create a dummy macro in the given directory css::uno::Reference xOutput = m_xFileAccess->openFileWrite(sDirectoryUri); // Make the script insert some text in all of the writer documents static constexpr OString sSource = "let context = uno.componentContext;\n" "let desktop = uno.idl.com.sun.star.frame.theDesktop(context);\n" "let enumeration = desktop.getComponents().createEnumeration();\n" "while (enumeration.hasMoreElements())\n" "{\n" " let model = enumeration.nextElement();\n" " model.getText().getEnd().setString(\"jsprovtest\");\n" "}\n"_ostr; css::uno::Sequence aSource(reinterpret_cast(sSource.getStr()), sSource.getLength()); xOutput->writeBytes(aSource); xOutput->closeOutput(); } void JsProvTest::testRun() { createDummyMacro(m_xUriHelper->getRootStorageURI() + "/MyScript.js"); // Get the macro as a property set css::uno::Reference xBrowseNode(m_xScriptProvider, css::uno::UNO_QUERY_THROW); css::uno::Reference xPropertySet( xBrowseNode->getChildNodes()[0]->getChildNodes()[0], css::uno::UNO_QUERY_THROW); // Get the XScript OUString sScriptUri; bool bConvertResult = xPropertySet->getPropertyValue("URI") >>= sScriptUri; CPPUNIT_ASSERT(bConvertResult); css::uno::Reference xScript = m_xScriptProvider->getScript(sScriptUri); CPPUNIT_ASSERT(xScript.is()); // Create a document to run the script on loadFromURL(u"private:factory/swriter"_ustr); // Execute it css::uno::Sequence aScriptParams; css::uno::Sequence aOutParamIndex; css::uno::Sequence aOutParam; xScript->invoke(aScriptParams, aOutParamIndex, aOutParam); // Check that the text was inserted into our test document css::uno::Reference xTextDocument(mxComponent, css::uno::UNO_QUERY_THROW); CPPUNIT_ASSERT_EQUAL(u"jsprovtest"_ustr, xTextDocument->getText()->getString()); } void JsProvTest::testUnrelatedFile() { // Create a file with a name that shouldn’t be picked up by the JavaScript provider OUString sFileUri = m_xUriHelper->getRootStorageURI() + "/MyScript.py"; m_xFileAccess->openFileWrite(sFileUri); CPPUNIT_ASSERT(m_xFileAccess->exists(sFileUri)); css::uno::Reference xBrowseNode(m_xScriptProvider, css::uno::UNO_QUERY_THROW); // The root node should have no children CPPUNIT_ASSERT_EQUAL(sal_Int32(0), xBrowseNode->getChildNodes().getLength()); } void JsProvTest::testFolder() { // Create a file that is nested in subfolders m_xFileAccess->openFileWrite(m_xUriHelper->getRootStorageURI() + "/very/nested/file/MyScript.js"); css::uno::Reference xBrowseNode(m_xScriptProvider, css::uno::UNO_QUERY_THROW); std::array aParts = { u"very"_ustr, u"nested"_ustr, u"file"_ustr }; for (const auto& sPart : aParts) { CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xBrowseNode->getChildNodes().getLength()); xBrowseNode = xBrowseNode->getChildNodes()[0]; CPPUNIT_ASSERT_EQUAL(css::script::browse::BrowseNodeTypes::CONTAINER, xBrowseNode->getType()); CPPUNIT_ASSERT_EQUAL(sPart, xBrowseNode->getName()); // Folders should be creatable but not editable CPPUNIT_ASSERT(isCreatable(xBrowseNode)); CPPUNIT_ASSERT(!isEditable(xBrowseNode)); } CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xBrowseNode->getChildNodes().getLength()); xBrowseNode = xBrowseNode->getChildNodes()[0]; CPPUNIT_ASSERT_EQUAL(u"MyScript"_ustr, xBrowseNode->getName()); } bool isCopyableNode(const css::uno::Reference& xNode) { css::uno::Reference xCopyable(xNode, css::uno::UNO_QUERY); return xCopyable.is() && xCopyable->isCopyableNode(); } void JsProvTest::testCopyable() { // Create a dummy macro in a subfolder of the script directory m_xFileAccess->createFolder(m_xUriHelper->getRootStorageURI() + "/SrcDir"); createDummyMacro(m_xUriHelper->getRootStorageURI() + "/SrcDir/MyScript.js"); css::uno::Reference xRootNode(m_xScriptProvider, css::uno::UNO_QUERY_THROW); // The root node shouldn’t be copyable CPPUNIT_ASSERT(!isCopyableNode(xRootNode)); css::uno::Reference xLibrary = xRootNode->getChildNodes()[0]; CPPUNIT_ASSERT_EQUAL(u"SrcDir"_ustr, xLibrary->getName()); // The library shouldn’t be copyable CPPUNIT_ASSERT(!isCopyableNode(xLibrary)); css::uno::Reference xFile = xLibrary->getChildNodes()[0]; CPPUNIT_ASSERT_EQUAL(u"MyScript"_ustr, xFile->getName()); // File nodes are copyable CPPUNIT_ASSERT(isCopyableNode(xFile)); css::uno::Reference xMacro = xFile->getChildNodes()[0]; CPPUNIT_ASSERT_EQUAL(u"MyScript"_ustr, xMacro->getName()); // Macro nodes are not copyable CPPUNIT_ASSERT(!isCopyableNode(xMacro)); css::uno::Reference xCopyable( xFile, css::uno::UNO_QUERY_THROW); // We can copy to the root level of the JavaScript hierarchy CPPUNIT_ASSERT(xCopyable->nodeCanBeCopiedTo(xRootNode)); // We can’t copy to the same directory that the file is already in CPPUNIT_ASSERT(!xCopyable->nodeCanBeCopiedTo(xLibrary)); // We can’t copy to a file CPPUNIT_ASSERT(!xCopyable->nodeCanBeCopiedTo(xFile)); // or a macro CPPUNIT_ASSERT(!xCopyable->nodeCanBeCopiedTo(xMacro)); // We shouldn’t be able to copy to any other language providers css::uno::Reference xMasterProviderFactory = css::script::provider::theMasterScriptProviderFactory::get(m_xContext); css::uno::Reference xMasterProvider( xMasterProviderFactory->createScriptProvider(css::uno::Any(u"user"_ustr)), css::uno::UNO_QUERY_THROW); for (const auto& xChild : xMasterProvider->getChildNodes()) { if (xChild->getName() == u"JavaScript"_ustr) continue; CPPUNIT_ASSERT(!xCopyable->nodeCanBeCopiedTo(xChild)); } // Make a new library that we can copy to m_xFileAccess->createFolder(m_xUriHelper->getRootStorageURI() + "/DestDir"); css::uno::Reference xOtherLibrary; for (const auto& xChild : xRootNode->getChildNodes()) { if (xChild->getName() == u"DestDir"_ustr) { xOtherLibrary = xChild; break; } } CPPUNIT_ASSERT(xOtherLibrary.is()); // We should be able to copy into the new library CPPUNIT_ASSERT(xCopyable->nodeCanBeCopiedTo(xOtherLibrary)); css::uno::Reference xCopiedNode = xCopyable->copyNode(xOtherLibrary); CPPUNIT_ASSERT_EQUAL(u"MyScript"_ustr, xCopiedNode->getName()); css::uno::Reference xCopiedMacro = xCopiedNode->getChildNodes()[0]; CPPUNIT_ASSERT_EQUAL(u"MyScript"_ustr, xCopiedMacro->getName()); // Get the URI of the copied macro css::uno::Reference xPropertySet(xCopiedMacro, css::uno::UNO_QUERY_THROW); css::uno::Any xURI = xPropertySet->getPropertyValue(u"URI"_ustr); OUString sURI; CPPUNIT_ASSERT(xURI >>= sURI); // The URI should contain the new library name CPPUNIT_ASSERT(sURI.indexOf(":DestDir|") != -1); // Create a document to run the script on loadFromURL(u"private:factory/swriter"_ustr); // Execute it css::uno::Sequence aScriptParams; css::uno::Sequence aOutParamIndex; css::uno::Sequence aOutParam; m_xScriptProvider->getScript(sURI)->invoke(aScriptParams, aOutParamIndex, aOutParam); // Check that the text was inserted into our test document css::uno::Reference xTextDocument(mxComponent, css::uno::UNO_QUERY_THROW); CPPUNIT_ASSERT_EQUAL(u"jsprovtest"_ustr, xTextDocument->getText()->getString()); } CPPUNIT_TEST_SUITE_REGISTRATION(JsProvTest); } // namespace CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */