diff --git a/build/azure-pipelines.yml b/build/azure-pipelines.yml index 8ece806816..2b4a5b4428 100644 --- a/build/azure-pipelines.yml +++ b/build/azure-pipelines.yml @@ -320,7 +320,7 @@ stages: - powershell: sqllocaldb start mssqllocaldb displayName: Start localdb (Windows only) condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT')) - - powershell: docker run --name mssql -d -p 1433:1433 -e ACCEPT_EULA=Y -e SA_PASSWORD=$(SA_PASSWORD) -e MSSQL_PID=Developer mcr.microsoft.com/mssql/server:2019-latest + - powershell: docker run --name mssql -d -p 1433:1433 -e ACCEPT_EULA=Y -e SA_PASSWORD=$(SA_PASSWORD) -e MSSQL_PID=Developer mcr.microsoft.com/mssql/server:2019-latest displayName: Start SQL Server (Linux only) condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) - task: DotNetCoreCLI@2 @@ -338,7 +338,6 @@ stages: - stage: E2E variables: npm_config_cache: $(Pipeline.Workspace)/.npm_e2e - CYPRESS_CACHE_FOLDER: $(Pipeline.Workspace)/cypress_binaries displayName: E2E Tests dependsOn: Build jobs: @@ -349,9 +348,9 @@ stages: - name: Umbraco__CMS__Unattended__InstallUnattended # Windows only value: true - name: Umbraco__CMS__Unattended__UnattendedUserName # Windows only - value: Cypress Test + value: Playwright Test - name: Umbraco__CMS__Unattended__UnattendedUserEmail # Windows only - value: cypress@umbraco.com + value: playwright@umbraco.com - name: Umbraco__CMS__Unattended__UnattendedUserPassword # Windows only value: UmbracoAcceptance123! - name: Umbraco__CMS__Global__InstallMissingDatabase # Windows only @@ -359,11 +358,11 @@ stages: - name: UmbracoDatabaseServer # Windows only value: (LocalDB)\MSSQLLocalDB - name: UmbracoDatabaseName # Windows only - value: Cypress + value: Playwright - name: ConnectionStrings__umbracoDbDSN # Windows only value: Server=$(UmbracoDatabaseServer);Database=$(UmbracoDatabaseName);Integrated Security=true; - - name: CYPRESS_BASE_URL - value: http://localhost:8080 + - name: PLAYWRIGHT_BASE_URL + value: https://localhost:8443 strategy: matrix: Linux: @@ -372,6 +371,7 @@ stages: dockerImageName: umbraco-linux Windows: vmImage: 'windows-latest' + DOTNET_GENERATE_ASPNET_CERTIFICATE: true # Automatically generate HTTPS development certificate on Windows pool: vmImage: $(vmImage) steps: @@ -392,19 +392,17 @@ stages: "npm_e2e" | "$(Agent.OS)" "npm_e2e" path: $(npm_config_cache) - - task: Cache@2 - displayName: Cache cypress binaries - inputs: - key: '"cypress_binaries" | "$(Agent.OS)" | $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/package-lock.json' - path: $(CYPRESS_CACHE_FOLDER) - task: PowerShell@2 - displayName: Generate Cypress.env.json + displayName: Generate .env inputs: targetType: inline - script: > - @{ username = "$(Umbraco__CMS__Unattended__UnattendedUserEmail)"; password = "$(Umbraco__CMS__Unattended__UnattendedUserPassword)" } | ConvertTo-Json | Set-Content -Path "tests/Umbraco.Tests.AcceptanceTest/cypress.env.json"# + workingDirectory: $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/ + script: | + New-Item -Path "." -Name ".env" -ItemType "file" -Value "UMBRACO_USER_LOGIN=$(Umbraco__CMS__Unattended__UnattendedUserEmail) + UMBRACO_USER_PASSWORD=$(Umbraco__CMS__Unattended__UnattendedUserPassword) + URL=$(PLAYWRIGHT_BASE_URL)" - script: npm ci --no-fund --no-audit --prefer-offline - workingDirectory: $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/ + workingDirectory: $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/ displayName: Run npm ci - powershell: sqllocaldb start mssqllocaldb displayName: Start localdb (Windows only) @@ -429,10 +427,13 @@ stages: docker build -t $(dockerImageName):$sha -f $(dockerfile) . mkdir -p $(Build.ArtifactStagingDirectory)/docker-images docker save -o $(Build.ArtifactStagingDirectory)/docker-images/$(dockerImageName).$sha.tar $(dockerImageName):$sha - docker run --name $(dockerImageName) -dp 8080:5000 -e UMBRACO__CMS__GLOBAL__ID=$(UMBRACO__CMS__GLOBAL__ID) $(dockerImageName):$sha + + # Manually generate HTTPS development certificate on Linux + dotnet dev-certs https -ep ${HOME}/.aspnet/https/aspnetapp.pfx -p UmbracoAcceptance123! + dotnet dev-certs https --trust + + docker run --name $(dockerImageName) -dp 8080:5000 -dp 8443:5001 -e UMBRACO__CMS__GLOBAL__ID=$(UMBRACO__CMS__GLOBAL__ID) -e ASPNETCORE_Kestrel__Certificates__Default__Password="UmbracoAcceptance123!" -e ASPNETCORE_Kestrel__Certificates__Default__Path=/https/aspnetapp.pfx -v ${HOME}/.aspnet/https:/https/ $(dockerImageName):$sha docker ps - # Windows containers take forever. - # --no-launch-profile stops ASPNETCORE_ENVIRONMENT=Development which breaks the users.ts tests (smtp config = invite user button) # Urls matching docker setup. - task: PowerShell@2 condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT')) @@ -442,10 +443,10 @@ stages: targetType: inline script: | dotnet new --install ./nupkg/Umbraco.Templates.*.nupkg - dotnet new umbraco --name Cypress -o . --no-restore + dotnet new umbraco --name Playwright -o . --no-restore dotnet restore --configfile ./nuget.config dotnet build --configuration $(buildConfiguration) --no-restore - Start-Process -FilePath "dotnet" -ArgumentList "run --configuration $(buildConfiguration) --no-build --no-launch-profile --urls $(CYPRESS_BASE_URL)" + Start-Process -FilePath "dotnet" -ArgumentList "run --configuration $(buildConfiguration) --no-build --no-launch-profile --urls $(PLAYWRIGHT_BASE_URL)" - task: PowerShell@2 displayName: Wait for app inputs: @@ -453,31 +454,33 @@ stages: workingDirectory: tests/Umbraco.Tests.AcceptanceTest script: | npm i -g wait-on - wait-on -v --interval 1000 --timeout 120000 $(CYPRESS_BASE_URL) + wait-on -v --interval 1000 --timeout 120000 $(PLAYWRIGHT_BASE_URL) - task: PowerShell@2 - displayName: Run Cypress (Desktop) + displayName: Install Playwright + inputs: + targetType: inline + workingDirectory: tests/Umbraco.Tests.AcceptanceTest + script: npx playwright install + - task: PowerShell@2 + displayName: Run Playwright (Desktop) continueOnError: true inputs: targetType: inline workingDirectory: tests/Umbraco.Tests.AcceptanceTest - script: 'npm run test -- --reporter junit --reporter-options "mochaFile=results/test-output-D-[hash].xml,toConsole=true" --config="viewportHeight=1600,viewportWidth=2560,screenshotsFolder=cypress/artifacts/desktop/screenshots,videosFolder=cypress/artifacts/desktop/videos,videoUploadOnPasses=false"' - - task: PublishTestResults@2 - displayName: Publish test results - condition: always() - inputs: - testResultsFormat: 'JUnit' - testResultsFiles: 'tests/Umbraco.Tests.AcceptanceTest/results/test-output-D-*.xml' - mergeTestResults: true - testRunTitle: "e2e - $(Agent.OS)" + script: 'npm run ui --ignore-certificate-errors' + - bash: | + if [ -f $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/results/ ]; then + echo "##vso[task.setVariable variable=myfileexists]true" + fi - task: CopyFiles@2 displayName: Prepare artifacts - condition: always() + condition: eq(variables.myfileexists, 'true') inputs: - sourceFolder: $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/cypress/artifacts - targetFolder: $(Build.ArtifactStagingDirectory)/cypresss + sourceFolder: $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/results/ + targetFolder: $(Build.ArtifactStagingDirectory)/playwright - task: PublishPipelineArtifact@1 displayName: "Publish test artifacts" - condition: always() + condition: eq(variables.myfileexists, 'true') inputs: targetPath: $(Build.ArtifactStagingDirectory) artifact: 'E2E artifacts - $(Agent.OS) - Attempt #$(System.JobAttempt)' diff --git a/tests/Umbraco.Tests.AcceptanceTest/config.js b/tests/Umbraco.Tests.AcceptanceTest/config.js index 5297cbccbc..b64608cd2e 100644 --- a/tests/Umbraco.Tests.AcceptanceTest/config.js +++ b/tests/Umbraco.Tests.AcceptanceTest/config.js @@ -20,7 +20,7 @@ const properties = [ ]; -const configPath = './cypress.env.json' +const configPath = './.env' console.log("Configure your test enviroment") @@ -29,13 +29,9 @@ prompt.start(); prompt.get(properties, function (error, result) { if (error) { return onError(error); } -var fileContent = `{ - "username": "${result.username}", - "password": "${result.password}"${ - result.baseUrl && `, - "baseUrl": "${result.baseUrl}"` - } -}`; +var fileContent = `UMBRACO_USER_LOGIN=${result.username} +UMBRACO_USER_PASSWORD=${result.password} +URL=${result.baseUrl || "https://localhost:44331"}`; fs.writeFile(configPath, fileContent, function (error) { if (error) return console.error(error); diff --git a/tests/Umbraco.Tests.AcceptanceTest/cypress.json b/tests/Umbraco.Tests.AcceptanceTest/cypress.json deleted file mode 100644 index 340eede2a0..0000000000 --- a/tests/Umbraco.Tests.AcceptanceTest/cypress.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "baseUrl": "https://localhost:44331", - "viewportHeight": 1024, - "viewportWidth": 1200, - "env": { - "username": "", - "password": "" - }, - "supportFile": "cypress/support/index.ts", - "videoUploadOnPasses" : false, - "retries": { - "runMode": 5, - "openMode": 1 - } -} diff --git a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Content/content.ts b/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Content/content.ts deleted file mode 100644 index 9288dd6f72..0000000000 --- a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Content/content.ts +++ /dev/null @@ -1,836 +0,0 @@ -/// -import { - AliasHelper, - ContentBuilder, - DocumentTypeBuilder, - GridDataTypeBuilder, - MacroBuilder, - PartialViewMacroBuilder, -} from 'umbraco-cypress-testhelpers'; - -context('Content', () => { - - beforeEach(() => { - cy.umbracoLogin(Cypress.env('username'), Cypress.env('password')); - }); - - function refreshContentTree() { - // Refresh to update the tree - cy.get('li .umb-tree-root:contains("Content")').should("be.visible").rightclick(); - cy.umbracoContextMenuAction("action-refreshNode").click(); - // We have to wait in case the execution is slow, otherwise we'll try and click the item before it appears in the UI - cy.get('.umb-tree-item__inner').should('exist', { timeout: 10000 }); - } - - function createSimpleMacro(name) { - const insertMacro = new PartialViewMacroBuilder() - .withName(name) - .withContent(`@inherits Umbraco.Cms.Web.Common.Macros.PartialViewMacroPage -

Acceptance test

`) - .build(); - - const macroWithPartial = new MacroBuilder() - .withName(name) - .withPartialViewMacro(insertMacro) - .withRenderInEditor() - .withUseInEditor() - .build(); - - cy.saveMacroWithPartial(macroWithPartial); - } - - it('Copy content', () => { - const rootDocTypeName = "Test document type"; - const childDocTypeName = "Child test document type"; - const nodeName = "1) Home"; - const childNodeName = "1) Child"; - const anotherNodeName = "2) Home"; - - const childDocType = new DocumentTypeBuilder() - .withName(childDocTypeName) - .build(); - - cy.deleteAllContent(); - cy.umbracoEnsureDocumentTypeNameNotExists(rootDocTypeName); - cy.umbracoEnsureDocumentTypeNameNotExists(childDocTypeName); - - cy.saveDocumentType(childDocType).then((generatedChildDocType) => { - const rootDocTypeAlias; - const createdChildDocType = generatedChildDocType; - - cy.get('li .umb-tree-root:contains("Content")').should("be.visible"); - - const rootDocType = new DocumentTypeBuilder() - .withName(rootDocTypeName) - .withAllowAsRoot(true) - .withAllowedContentTypes(createdChildDocType["id"]) - .build(); - - cy.saveDocumentType(rootDocType).then((generatedRootDocType) => { - rootDocTypeAlias = generatedRootDocType["alias"]; - - const rootContentNode = new ContentBuilder() - .withContentTypeAlias(rootDocTypeAlias) - .withAction("saveNew") - .addVariant() - .withName(nodeName) - .withSave(true) - .done() - .build(); - - cy.saveContent(rootContentNode).then((contentNode) => { - // Add an item under root node - const childContentNode = new ContentBuilder() - .withContentTypeAlias(createdChildDocType["alias"]) - .withAction("saveNew") - .withParent(contentNode["id"]) - .addVariant() - .withName(childNodeName) - .withSave(true) - .done() - .build(); - - cy.saveContent(childContentNode); - }); - - const anotherRootContentNode = new ContentBuilder() - .withContentTypeAlias(rootDocTypeAlias) - .withAction("saveNew") - .addVariant() - .withName(anotherNodeName) - .withSave(true) - .done() - .build(); - - cy.saveContent(anotherRootContentNode); - }); - }); - - // Refresh to update the tree - refreshContentTree(); - - // Copy node - cy.umbracoTreeItem("content", [nodeName, childNodeName]).rightclick({ force: true }); - cy.umbracoContextMenuAction("action-copy").click(); - cy.get('.umb-pane [data-element="tree-item-' + anotherNodeName + '"]').click(); - cy.get('.umb-dialog-footer > .btn-primary').click(); - - // Assert - cy.get('.alert-success').should('exist'); - - // Clean up (content is automatically deleted when document types are gone) - cy.umbracoEnsureDocumentTypeNameNotExists(rootDocTypeName); - cy.umbracoEnsureDocumentTypeNameNotExists(childDocTypeName); - }); - - it('Move content', () => { - const rootDocTypeName = "Test document type"; - const childDocTypeName = "Child test document type"; - const nodeName = "1) Home"; - const childNodeName = "1) Child"; - const anotherNodeName = "2) Home"; - - const childDocType = new DocumentTypeBuilder() - .withName(childDocTypeName) - .build(); - - cy.deleteAllContent(); - cy.umbracoEnsureDocumentTypeNameNotExists(rootDocTypeName); - cy.umbracoEnsureDocumentTypeNameNotExists(childDocTypeName); - - cy.saveDocumentType(childDocType).then((generatedChildDocType) => { - const rootDocTypeAlias; - const createdChildDocType = generatedChildDocType; - - cy.get('li .umb-tree-root:contains("Content")').should("be.visible"); - - const rootDocType = new DocumentTypeBuilder() - .withName(rootDocTypeName) - .withAllowAsRoot(true) - .withAllowedContentTypes(createdChildDocType["id"]) - .build(); - - cy.saveDocumentType(rootDocType).then((generatedRootDocType) => { - rootDocTypeAlias = generatedRootDocType["alias"]; - - const rootContentNode = new ContentBuilder() - .withContentTypeAlias(rootDocTypeAlias) - .withAction("saveNew") - .addVariant() - .withName(nodeName) - .withSave(true) - .done() - .build(); - - cy.saveContent(rootContentNode).then((contentNode) => { - // Add an item under root node - const childContentNode = new ContentBuilder() - .withContentTypeAlias(createdChildDocType["alias"]) - .withAction("saveNew") - .withParent(contentNode["id"]) - .addVariant() - .withName(childNodeName) - .withSave(true) - .done() - .build(); - - cy.saveContent(childContentNode); - }); - - const anotherRootContentNode = new ContentBuilder() - .withContentTypeAlias(rootDocTypeAlias) - .withAction("saveNew") - .addVariant() - .withName(anotherNodeName) - .withSave(true) - .done() - .build(); - - cy.saveContent(anotherRootContentNode); - }); - }); - - // Refresh to update the tree - refreshContentTree(); - - // Move node - cy.umbracoTreeItem("content", [nodeName, childNodeName]).rightclick({ force: true }); - cy.umbracoContextMenuAction("action-move").click(); - cy.get('.umb-pane [data-element="tree-item-' + anotherNodeName + '"]').click(); - cy.get('.umb-dialog-footer > .btn-primary').click(); - - // Assert - cy.get('.alert-success').should('exist'); - - // Clean up (content is automatically deleted when document types are gone) - cy.umbracoEnsureDocumentTypeNameNotExists(rootDocTypeName); - cy.umbracoEnsureDocumentTypeNameNotExists(childDocTypeName); - }); - - it('Sort content', () => { - const rootDocTypeName = "Test document type"; - const childDocTypeName = "Child test document type"; - const nodeName = "1) Home"; - const firstChildNodeName = "1) Child"; - const secondChildNodeName = "2) Child"; - - const childDocType = new DocumentTypeBuilder() - .withName(childDocTypeName) - .build(); - - cy.deleteAllContent(); - cy.umbracoEnsureDocumentTypeNameNotExists(rootDocTypeName); - cy.umbracoEnsureDocumentTypeNameNotExists(childDocTypeName); - - cy.saveDocumentType(childDocType).then((generatedChildDocType) => { - const createdChildDocType = generatedChildDocType; - - cy.get('li .umb-tree-root:contains("Content")').should("be.visible"); - - const rootDocType = new DocumentTypeBuilder() - .withName(rootDocTypeName) - .withAllowAsRoot(true) - .withAllowedContentTypes(createdChildDocType["id"]) - .build(); - - cy.saveDocumentType(rootDocType).then((generatedRootDocType) => { - const parentId; - - const rootContentNode = new ContentBuilder() - .withContentTypeAlias(generatedRootDocType["alias"]) - .withAction("saveNew") - .addVariant() - .withName(nodeName) - .withSave(true) - .done() - .build(); - - cy.saveContent(rootContentNode).then((contentNode) => { - parentId = contentNode["id"]; - - // Add an item under root node - const firstChildContentNode = new ContentBuilder() - .withContentTypeAlias(createdChildDocType["alias"]) - .withAction("saveNew") - .withParent(parentId) - .addVariant() - .withName(firstChildNodeName) - .withSave(true) - .done() - .build(); - - cy.saveContent(firstChildContentNode); - - // Add a second item under root node - const secondChildContentNode = new ContentBuilder() - .withContentTypeAlias(createdChildDocType["alias"]) - .withAction("saveNew") - .withParent(parentId) - .addVariant() - .withName(secondChildNodeName) - .withSave(true) - .done() - .build(); - - cy.saveContent(secondChildContentNode); - }); - }); - }); - - // Refresh to update the tree - refreshContentTree(); - - // Sort nodes - cy.umbracoTreeItem("content", [nodeName]).rightclick({ force: true }); - cy.umbracoContextMenuAction("action-sort").click(); - - //Drag and drop - cy.get('.ui-sortable .ui-sortable-handle :nth-child(2)').eq(0).trigger('mousedown', { which: 1 }) - cy.get('.ui-sortable .ui-sortable-handle :nth-child(2)').eq(1).trigger("mousemove").trigger("mouseup") - - // Save and close dialog - cy.get('.umb-modalcolumn .btn-success').click(); - cy.get('.umb-modalcolumn .btn-link').click(); - - // Assert - cy.get('.umb-tree-item [node="child"]').eq(0).should('contain.text', secondChildNodeName); - cy.get('.umb-tree-item [node="child"]').eq(1).should('contain.text', firstChildNodeName); - - // Clean up (content is automatically deleted when document types are gone) - cy.umbracoEnsureDocumentTypeNameNotExists(rootDocTypeName); - cy.umbracoEnsureDocumentTypeNameNotExists(childDocTypeName); - }); - - it('Rollback content', () => { - const rootDocTypeName = "Test document type"; - const initialNodeName = "Home node"; - const nodeName = "Home"; - - const rootDocType = new DocumentTypeBuilder() - .withName(rootDocTypeName) - .withAllowAsRoot(true) - .build(); - - cy.deleteAllContent(); - cy.umbracoEnsureDocumentTypeNameNotExists(rootDocTypeName); - - cy.saveDocumentType(rootDocType).then((generatedRootDocType) => { - const rootContentNode = new ContentBuilder() - .withContentTypeAlias(generatedRootDocType["alias"]) - .addVariant() - .withName(initialNodeName) - .withSave(true) - .done() - .build(); - - cy.saveContent(rootContentNode) - }); - - // Refresh to update the tree - refreshContentTree(); - - // Access node - cy.umbracoTreeItem("content", [initialNodeName]).click(); - - // Edit header - cy.get('#headerName').clear(); - cy.umbracoEditorHeaderName(nodeName); - - // Save and publish - cy.get('.btn-success').first().click(); - cy.umbracoSuccessNotification().should('be.visible'); - - // Rollback - cy.get('.umb-box-header :button').click(); - - // Wait to ensure the next button is clickable - cy.wait(1000); - - cy.get('.-selectable.cursor-pointer:first').click(); - - // Wait to ensure the next button is clickable - cy.wait(1000); - - cy.get('.umb-editor-footer-content__right-side > [button-style="success"] > .umb-button > .btn-success').click(); - - refreshContentTree(); - - // Assert - cy.get('.history').find('.umb-badge').contains('Save').should('be.visible'); - cy.get('.history').find('.umb-badge').contains('Rollback').should('be.visible'); - cy.get('#headerName').should('have.value', initialNodeName); - - // Clean up (content is automatically deleted when document types are gone) - cy.umbracoEnsureDocumentTypeNameNotExists(rootDocTypeName); - }); - - it('View audit trail', () => { - const rootDocTypeName = "Test document type"; - const nodeName = "Home"; - const labelName = "Name"; - - const rootDocType = new DocumentTypeBuilder() - .withName(rootDocTypeName) - .withAllowAsRoot(true) - .addGroup() - .addTextBoxProperty() - .withLabel(labelName) - .done() - .done() - .build(); - - cy.deleteAllContent(); - cy.umbracoEnsureDocumentTypeNameNotExists(rootDocTypeName); - - cy.saveDocumentType(rootDocType).then((generatedRootDocType) => { - const rootContentNode = new ContentBuilder() - .withContentTypeAlias(generatedRootDocType["alias"]) - .addVariant() - .withName(nodeName) - .withSave(true) - .done() - .build(); - - cy.saveContent(rootContentNode) - }); - - // Refresh to update the tree - refreshContentTree(); - - // Access node - cy.umbracoTreeItem("content", [nodeName]).click(); - - // Navigate to Info app - cy.get(':nth-child(2) > [ng-show="navItem.alias !== \'more\'"]').click(); - - // Assert - cy.get('.history').should('exist'); - - // Clean up (content is automatically deleted when document types are gone) - cy.umbracoEnsureDocumentTypeNameNotExists(rootDocTypeName); - }); - - it('Save draft', () => { - const rootDocTypeName = "Test document type"; - const nodeName = "Home"; - - const rootDocType = new DocumentTypeBuilder() - .withName(rootDocTypeName) - .withAllowAsRoot(true) - .build(); - - cy.deleteAllContent(); - cy.umbracoEnsureDocumentTypeNameNotExists(rootDocTypeName); - - cy.saveDocumentType(rootDocType).then((generatedRootDocType) => { - const rootContentNode = new ContentBuilder() - .withContentTypeAlias(generatedRootDocType["alias"]) - .withAction("saveNew") - .addVariant() - .withName(nodeName) - .withSave(true) - .done() - .build(); - - cy.saveContent(rootContentNode) - }); - - // Refresh to update the tree - refreshContentTree(); - - // Access node - cy.umbracoTreeItem("content", [nodeName]).click(); - - // Assert - cy.get('[data-element="node-info-status"]').find('.umb-badge').should('contain.text', "Unpublished"); - - // Clean up (content is automatically deleted when document types are gone) - cy.umbracoEnsureDocumentTypeNameNotExists(rootDocTypeName); - }); - - it('Preview draft', () => { - const rootDocTypeName = "Test document type"; - const nodeName = "Home"; - - const rootDocType = new DocumentTypeBuilder() - .withName(rootDocTypeName) - .withAllowAsRoot(true) - .build(); - - cy.deleteAllContent(); - cy.umbracoEnsureDocumentTypeNameNotExists(rootDocTypeName); - - cy.saveDocumentType(rootDocType).then((generatedRootDocType) => { - const rootContentNode = new ContentBuilder() - .withContentTypeAlias(generatedRootDocType["alias"]) - .withAction("saveNew") - .addVariant() - .withName(nodeName) - .withSave(true) - .done() - .build(); - - cy.saveContent(rootContentNode) - }); - - // Refresh to update the tree - refreshContentTree(); - - // Access node - cy.umbracoTreeItem("content", [nodeName]).click(); - - // Preview - cy.get('[alias="preview"]').should('be.visible').click(); - - // Assert - cy.umbracoSuccessNotification({ multiple: true }).should('be.visible'); - - // Clean up (content is automatically deleted when document types are gone) - cy.umbracoEnsureDocumentTypeNameNotExists(rootDocTypeName); - }); - - it('Publish draft', () => { - const rootDocTypeName = "Test document type"; - const nodeName = "Home"; - - const rootDocType = new DocumentTypeBuilder() - .withName(rootDocTypeName) - .withAllowAsRoot(true) - .build(); - - cy.deleteAllContent(); - cy.umbracoEnsureDocumentTypeNameNotExists(rootDocTypeName); - - cy.saveDocumentType(rootDocType).then((generatedRootDocType) => { - const rootContentNode = new ContentBuilder() - .withContentTypeAlias(generatedRootDocType["alias"]) - .addVariant() - .withName(nodeName) - .withSave(true) - .done() - .build(); - - cy.saveContent(rootContentNode) - }); - - // Refresh to update the tree - refreshContentTree(); - - // Access node - cy.umbracoTreeItem("content", [nodeName]).click(); - - // Assert - cy.get('[data-element="node-info-status"]').find('.umb-badge').should('contain.text', "Published"); - - // Clean up (content is automatically deleted when document types are gone) - cy.umbracoEnsureDocumentTypeNameNotExists(rootDocTypeName); - }); - - it('Content with contentpicker', () => { - const pickerDocTypeName = 'Content picker doc type'; - const pickerDocTypeAlias = AliasHelper.toAlias(pickerDocTypeName); - const pickedDocTypeName = 'Picked content document type'; - const pickedDocTypeAlias = AliasHelper.toAlias(pickedDocTypeName); - cy.deleteAllContent(); - cy.umbracoEnsureDocumentTypeNameNotExists(pickerDocTypeName); - cy.umbracoEnsureTemplateNameNotExists(pickerDocTypeName); - cy.umbracoEnsureDocumentTypeNameNotExists(pickedDocTypeName); - - // Create the content type and content we'll be picking from. - const pickedDocType = new DocumentTypeBuilder() - .withName(pickedDocTypeName) - .withAlias(pickedDocTypeAlias) - .withAllowAsRoot(true) - .addGroup() - .addTextBoxProperty() - .withAlias('text') - .done() - .done() - .build(); - - cy.saveDocumentType(pickedDocType).then((generatedType) => { - const pickedContentNode = new ContentBuilder() - .withContentTypeAlias(generatedType["alias"]) - .withAction("publishNew") - .addVariant() - .withName('Content to pick') - .withSave(true) - .withPublish(true) - .addProperty() - .withAlias('text') - .withValue('Acceptance test') - .done() - .withSave(true) - .withPublish(true) - .done() - .build(); - cy.saveContent(pickedContentNode); - }); - - // Create the doctype with a the picker - const pickerDocType = new DocumentTypeBuilder() - .withName(pickerDocTypeName) - .withAlias(pickerDocTypeAlias) - .withAllowAsRoot(true) - .withDefaultTemplate(pickerDocTypeAlias) - .addGroup() - .withName('ContentPickerGroup') - .addContentPickerProperty() - .withAlias('picker') - .done() - .done() - .build(); - - cy.saveDocumentType(pickerDocType); - - // Edit it the template to allow us to verify the rendered view. - cy.editTemplate(pickerDocTypeName, `@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage - @{ - Layout = null; - var pickedItem = Model.Picker as PickedContentDocumentType; - } - -

@pickedItem.Text

`); - - // Create content with content picker - cy.get('.umb-tree-root-link').rightclick(); - cy.get('[data-element="action-create"]').click(); - cy.get('[data-element="action-create-' + pickerDocTypeAlias + '"] > .umb-action-link').click(); - // Fill out content - cy.umbracoEditorHeaderName('ContentPickerContent'); - cy.get('.umb-node-preview-add').click(); - // Should really try and find a better way to do this, but umbracoTreeItem tries to click the content pane in the background - cy.get('[ng-if="vm.treeReady"] > .umb-tree .umb-tree-item__inner').click(); - // We have to wait for the picked content to show up or it wont be added. - cy.get('.umb-node-preview__description').should('be.visible'); - //save and publish - cy.umbracoButtonByLabelKey('buttons_saveAndPublish').click(); - cy.umbracoSuccessNotification().should('be.visible'); - - // Assert - cy.log('Checking that content is rendered correctly.') - const expectedContent = '

Acceptance test

' - cy.umbracoVerifyRenderedViewContent('contentpickercontent', expectedContent, true).should('be.true'); - // clean - cy.umbracoEnsureDocumentTypeNameNotExists(pickerDocTypeName); - cy.umbracoEnsureTemplateNameNotExists(pickerDocTypeName); - cy.umbracoEnsureDocumentTypeNameNotExists(pickedDocTypeName); - }); - - it('Content with macro in RTE', () => { - const viewMacroName = 'Content with macro in RTE'; - const partialFileName = viewMacroName + '.cshtml'; - - cy.umbracoEnsureMacroNameNotExists(viewMacroName); - cy.umbracoEnsurePartialViewMacroFileNameNotExists(partialFileName); - cy.umbracoEnsureDocumentTypeNameNotExists(viewMacroName); - cy.umbracoEnsureTemplateNameNotExists(viewMacroName); - cy.deleteAllContent(); - - // First thing first we got to create the macro we will be inserting - createSimpleMacro(viewMacroName); - - // Now we need to create a document type with a rich text editor where we can insert the macro - // The document type must have a template as well in order to ensure that the content is displayed correctly - const alias = AliasHelper.toAlias(viewMacroName); - const docType = new DocumentTypeBuilder() - .withName(viewMacroName) - .withAlias(alias) - .withAllowAsRoot(true) - .withDefaultTemplate(alias) - .addGroup() - .addRichTextProperty() - .withAlias('text') - .done() - .done() - .build(); - - cy.saveDocumentType(docType).then((generatedDocType) => { - // Might as wel initally create the content here, the less GUI work during the test the better - const contentNode = new ContentBuilder() - .withContentTypeAlias(generatedDocType["alias"]) - .withAction('saveNew') - .addVariant() - .withName(viewMacroName) - .withSave(true) - .done() - .build(); - - cy.saveContent(contentNode); - }); - - // Edit the macro template in order to have something to verify on when rendered. - cy.editTemplate(viewMacroName, `@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage -@{ - Layout = null; -} -@{ - if (Model.HasValue("text")){ - @(Model.Value("text")) - } -} `); - - // Enter content - refreshContentTree(); - cy.umbracoTreeItem("content", [viewMacroName]).click(); - - // Insert macro - cy.get('#mceu_13-button').click(); - cy.get('.umb-card-grid-item').contains(viewMacroName).click(); - - // Save and publish - cy.umbracoButtonByLabelKey('buttons_saveAndPublish').click(); - cy.umbracoSuccessNotification().should('be.visible'); - - // Ensure that the view gets rendered correctly - const expected = `

Acceptance test

 

`; - cy.umbracoVerifyRenderedViewContent('/', expected, true).should('be.true'); - - // Cleanup - cy.umbracoEnsureMacroNameNotExists(viewMacroName); - cy.umbracoEnsurePartialViewMacroFileNameNotExists(partialFileName); - cy.umbracoEnsureDocumentTypeNameNotExists(viewMacroName); - cy.umbracoEnsureTemplateNameNotExists(viewMacroName); - }); - - it('Content with macro in grid', () => { - const name = 'Content with macro in grid'; - const macroName = 'Grid macro'; - const macroFileName = macroName + '.cshtml'; - - cy.umbracoEnsureDataTypeNameNotExists(name); - cy.umbracoEnsureDocumentTypeNameNotExists(name); - cy.umbracoEnsureTemplateNameNotExists(name); - cy.umbracoEnsureMacroNameNotExists(macroName); - cy.umbracoEnsurePartialViewMacroFileNameNotExists(macroFileName); - cy.deleteAllContent(); - - createSimpleMacro(macroName); - - const grid = new GridDataTypeBuilder() - .withName(name) - .withDefaultGrid() - .build(); - - const alias = AliasHelper.toAlias(name); - // Save grid and get the ID - cy.saveDataType(grid).then((dataType) => { - // Create a document type using the data type - const docType = new DocumentTypeBuilder() - .withName(name) - .withAlias(alias) - .withAllowAsRoot(true) - .withDefaultTemplate(alias) - .addGroup() - .addCustomProperty(dataType['id']) - .withAlias('grid') - .done() - .done() - .build(); - - cy.saveDocumentType(docType).then((generatedDocType) => { - const contentNode = new ContentBuilder() - .withContentTypeAlias(generatedDocType["alias"]) - .addVariant() - .withName(name) - .withSave(true) - .done() - .build(); - - cy.saveContent(contentNode); - }); - }); - - // Edit the template to allow us to verify the rendered view - cy.editTemplate(name, `@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage - @{ - Layout = null; - } -@Html.GetGridHtml(Model, "grid")`); - - // Act - // Enter content - refreshContentTree(); - cy.umbracoTreeItem("content", [name]).click(); - // Click add - cy.get(':nth-child(2) > .preview-row > .preview-col > .preview-cell').click(); // Choose 1 column layout. - cy.get('.umb-column > .templates-preview > :nth-child(2) > small').click(); // Choose headline - cy.get('.umb-cell-placeholder').click(); - // Click macro - cy.get(':nth-child(4) > .umb-card-grid-item > :nth-child(1)').click(); - // Select the macro - cy.get(`.umb-card-grid-item[title='${macroName}']`).click('bottom'); - - - // Save and publish - cy.umbracoButtonByLabelKey('buttons_saveAndPublish').click(); - cy.umbracoSuccessNotification().should('be.visible'); - - const expected = ` -
-
-
-
-
-
-
-

Acceptance test

-
-
-
-
-
-
-
` - - cy.umbracoVerifyRenderedViewContent('/', expected, true).should('be.true'); - - // Clean - cy.umbracoEnsureDataTypeNameNotExists(name); - cy.umbracoEnsureDocumentTypeNameNotExists(name); - cy.umbracoEnsureTemplateNameNotExists(name); - cy.umbracoEnsureMacroNameNotExists(macroName); - cy.umbracoEnsurePartialViewMacroFileNameNotExists(macroFileName); - }); - - it('Create content with the same name', () => { - const documentTypeName = "TestType"; - const nodeName = "Home"; - const expectedNodeName = nodeName + " (1)"; - - cy.umbracoEnsureDocumentTypeNameNotExists(documentTypeName); - cy.deleteAllContent(); - - const documentType = new DocumentTypeBuilder() - .withName(documentTypeName) - .withAllowAsRoot(true) - .build(); - - cy.saveDocumentType(documentType).then((generatedDocumentType) => { - const rootContentNode = new ContentBuilder() - .withAction("saveNew") - .withContentTypeAlias(generatedDocumentType["alias"]) - .addVariant() - .withName(nodeName) - .withSave(true) - .done() - .build(); - - cy.saveContent(rootContentNode) - }); - - refreshContentTree(); - - cy.get('li .umb-tree-root:contains("Content")').should("be.visible").rightclick(); - cy.umbracoContextMenuAction("action-create").click(); - cy.get('.umb-action-link').contains(documentTypeName).click(); - cy.umbracoEditorHeaderName(nodeName); - cy.umbracoButtonByLabelKey("buttons_saveAndPublish").click(); - - cy.umbracoSuccessNotification().should('be.visible'); - cy.umbracoTreeItem("content", [expectedNodeName]).should('be.visible'); - - cy.umbracoEnsureDocumentTypeNameNotExists(documentTypeName); - cy.deleteAllContent(); - }); -}); diff --git a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Content/recycleBin.ts b/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Content/recycleBin.ts deleted file mode 100644 index 292d54acc0..0000000000 --- a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Content/recycleBin.ts +++ /dev/null @@ -1,78 +0,0 @@ -/// -import { - ContentBuilder, - DocumentTypeBuilder, -} from 'umbraco-cypress-testhelpers'; - -context('Recycle bin', () => { - - beforeEach(() => { - cy.umbracoLogin(Cypress.env('username'), Cypress.env('password')); - }); - - function refreshContentTree() { - // Refresh to update the tree - cy.get('li .umb-tree-root:contains("Content")').should("be.visible").rightclick(); - cy.umbracoContextMenuAction("action-refreshNode").click(); - // We have to wait in case the execution is slow, otherwise we'll try and click the item before it appears in the UI - cy.get('.umb-tree-item__inner').should('exist', { timeout: 10000 }); - } - - it('Can delete content from recycle bin', () => { - const contentToDeleteName = "DeleteMe"; - const contentToNotDeleteName = "DontDelete"; - const testType = "TestType"; - - cy.umbracoEnsureDocumentTypeNameNotExists(testType); - cy.deleteAllContent(); - - const docType = new DocumentTypeBuilder() - .withName(testType) - .build(); - - cy.saveDocumentType(docType).then((savedDocType) => { - const contentToDelete = new ContentBuilder() - .withContentTypeAlias(savedDocType.alias) - .withAction("saveNew") - .addVariant() - .withName(contentToDeleteName) - .withSave(true) - .done() - .build(); - - const contentToNotDelete = new ContentBuilder() - .withContentTypeAlias(savedDocType.alias) - .withAction("saveNew") - .addVariant() - .withName(contentToNotDeleteName) - .withSave(true) - .done() - .build(); - - // Put it in the recycle bin - cy.saveContent(contentToDelete).then(savedToDelete => { - cy.deleteContentById(savedToDelete.id); - }); - cy.saveContent(contentToNotDelete).then(savedNotToDelete => { - cy.deleteContentById(savedNotToDelete.id) - }); - }); - - refreshContentTree(); - cy.umbracoTreeItem('content', ["Recycle Bin"]).click(); - cy.get('.umb-content-grid__content').contains(contentToDeleteName).closest('div').click(); - cy.umbracoButtonByLabelKey('actions_delete').click(); - cy.umbracoButtonByLabelKey('contentTypeEditor_yesDelete').click(); - - cy.umbracoSuccessNotification().should('be.visible'); - - cy.get('.umb-content-grid__content').contains(contentToDeleteName).should('not.exist'); - cy.umbracoTreeItem('content', ["Recycle Bin", contentToDeleteName]).should('not.exist'); - - cy.get('.umb-content-grid__content').contains(contentToNotDeleteName).should('be.visible'); - cy.umbracoTreeItem('content', ["Recycle Bin", contentToNotDeleteName]).should('be.visible'); - - cy.deleteAllContent(); - cy.umbracoEnsureDocumentTypeNameNotExists(testType); - }); -}); diff --git a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Content/routing.ts b/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Content/routing.ts deleted file mode 100644 index 25ec683606..0000000000 --- a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Content/routing.ts +++ /dev/null @@ -1,369 +0,0 @@ -/// -import { - DocumentTypeBuilder, - ContentBuilder -} from 'umbraco-cypress-testhelpers'; - -context('Routing', () => { - - let swedishLanguageId = 0; - const swedishCulture = "sv"; - const danishCulture = "da" - const nodeName = "Root"; - const childNodeName = "Child"; - const grandChildNodeName = "Grandchild"; - const rootDocTypeName = "Test document type"; - - function refreshContentTree() { - // Refresh to update the tree - cy.get('li .umb-tree-root:contains("Content")').should("be.visible").rightclick(); - cy.umbracoContextMenuAction("action-refreshNode").click(); - // We have to wait in case the execution is slow, otherwise we'll try and click the item before it appears in the UI - cy.get('.umb-tree-item__inner').should('exist', {timeout: 10000}); - } - - function saveNewLanguages() { - // Save Danish - const url = "/umbraco/backoffice/umbracoapi/language/SaveLanguage"; - const danishRequestBody = { - culture: danishCulture - } - - cy.umbracoApiRequest(url, "POST", danishRequestBody); - - // Save Swedish - const swedishRequestBody = { - culture: swedishCulture - } - - cy.umbracoApiRequest(url, "POST", swedishRequestBody).then((responseBody) => { - swedishLanguageId = responseBody["id"]; - }); - } - - function configureDomain(id, name, lang) { - //Save domain for child node - const url = "/umbraco/backoffice/umbracoapi/content/PostSaveLanguageAndDomains" - const body = { - nodeId : id, - domains : [ - { - name : name, - lang : lang - }], - language : 0 - } - cy.umbracoApiRequest(url, 'POST', body); - } - - beforeEach(() => { - cy.umbracoLogin(Cypress.env('username'), Cypress.env('password')); - // Ensure cleaned before tests run - cy.deleteAllContent(); - cy.umbracoEnsureDocumentTypeNameNotExists(rootDocTypeName); - cy.umbracoEnsureLanguageCultureNotExists(danishCulture); - cy.umbracoEnsureLanguageCultureNotExists(swedishCulture); - }); - - afterEach(() => { - // Cleanup after tests - cy.deleteAllContent(); - cy.umbracoEnsureDocumentTypeNameNotExists(rootDocTypeName); - cy.umbracoEnsureLanguageCultureNotExists(danishCulture); - cy.umbracoEnsureLanguageCultureNotExists(swedishCulture); - }) - - it('Root node published in language A, Child node published in language A', () => { - - const rootDocType = new DocumentTypeBuilder() - .withName(rootDocTypeName) - .withAllowAsRoot(true) - .withAllowCultureVariation(true) - .build(); - - saveNewLanguages(); - - cy.saveDocumentType(rootDocType).then((generatedRootDocType) => { - const rootContentNode = new ContentBuilder() - .withContentTypeAlias(generatedRootDocType["alias"]) - .withAction("publishNew") - .addVariant() - .withCulture('en-US') - .withName(nodeName) - .withSave(true) - .withPublish(true) - .done() - .build(); - - cy.saveContent(rootContentNode).then((generatedRootContent) => { - const childContentNode = new ContentBuilder() - .withContentTypeAlias(generatedRootDocType["alias"]) - .withAction("saveNew") - .withParent(generatedRootContent["id"]) - .addVariant() - .withCulture('en-US') - .withName(childNodeName) - .withSave(true) - .done() - .build(); - - cy.saveContent(childContentNode); - }); - }); - - // Refresh to update the tree - refreshContentTree(); - - cy.umbracoTreeItem("content", [nodeName, childNodeName]).click(); - cy.umbracoButtonByLabelKey('buttons_saveAndPublish').click(); - // Pop-up with what cultures you want to publish shows, click it - cy.umbracoButtonByLabelKey('buttons_saveAndPublish').last().click(); - - // Assert - cy.get('.alert-success').should('exist'); - }); - - - it('Root node published in language A, Child node published in language B', () => { - - const rootDocType = new DocumentTypeBuilder() - .withName(rootDocTypeName) - .withAllowAsRoot(true) - .withAllowCultureVariation(true) - .build(); - - saveNewLanguages(); - - cy.saveDocumentType(rootDocType).then((generatedRootDocType) => { - const rootContentNode = new ContentBuilder() - .withContentTypeAlias(generatedRootDocType["alias"]) - .withAction("publishNew") - .addVariant() - .withCulture('en-US') - .withName(nodeName) - .withSave(true) - .withPublish(true) - .done() - .build(); - - cy.saveContent(rootContentNode).then((generatedRootContent) => { - const childContentNode = new ContentBuilder() - .withContentTypeAlias(generatedRootDocType["alias"]) - .withAction("saveNew") - .withParent(generatedRootContent["id"]) - .addVariant() - .withCulture('en-US') - .withName(childNodeName) - .withSave(true) - .done() - .addVariant() - .withCulture(swedishCulture) - .withName("Bärn") - .withSave(true) - .done() - .build(); - - cy.saveContent(childContentNode); - }); - }); - - // Refresh to update the tree - refreshContentTree(); - - cy.umbracoTreeItem("content", [nodeName, childNodeName]).click(); - cy.umbracoButtonByLabelKey('buttons_saveAndPublish').click(); - // Pop-up with what cultures you want to publish shows, click it - cy.get('.umb-list').should('be.visible'); - cy.get('.checkbox').last().click(); - cy.umbracoButtonByLabelKey('buttons_saveAndPublish').last().click(); - - // Assert - cy.get('.alert-success').should('have.length', 2); - cy.get('.alert-warning').should('exist'); - }); - - it('Root node published in language A, Child node published in language A + B, Grandchild published in A + B', () => { - - const rootDocType = new DocumentTypeBuilder() - .withName(rootDocTypeName) - .withAllowAsRoot(true) - .withAllowCultureVariation(true) - .build(); - - saveNewLanguages(); - - cy.saveDocumentType(rootDocType).then((generatedRootDocType) => { - const rootContentNode = new ContentBuilder() - .withContentTypeAlias(generatedRootDocType["alias"]) - .withAction("publishNew") - .addVariant() - .withCulture('en-US') - .withName(nodeName) - .withSave(true) - .withPublish(true) - .done() - .build(); - - cy.saveContent(rootContentNode).then((generatedRootContent) => { - - configureDomain(generatedRootContent["id"], "/en", 1); - const childContentNode = new ContentBuilder() - .withContentTypeAlias(generatedRootDocType["alias"]) - .withAction("saveNew") - .withParent(generatedRootContent["id"]) - .addVariant() - .withCulture('en-US') - .withName(childNodeName) - .withSave(true) - .done() - .addVariant() - .withCulture(swedishCulture) - .withName("Barn") - .withSave(true) - .done() - .build(); - - cy.saveContent(childContentNode).then((generatedChildContent) => { - - configureDomain(generatedChildContent["id"], "/sv", swedishLanguageId); - const grandChildContentNode = new ContentBuilder() - .withContentTypeAlias(generatedRootDocType["alias"]) - .withAction("saveNew") - .withParent(generatedChildContent["id"]) - .addVariant() - .withCulture('en-US') - .withName(grandChildNodeName) - .withSave(true) - .done() - .addVariant() - .withCulture(swedishCulture) - .withName("Barnbarn") - .withSave(true) - .done() - .build(); - - cy.saveContent(grandChildContentNode); - }); - }); - }); - - // Refresh to update the tree - refreshContentTree(); - - // Publish Child - cy.umbracoTreeItem("content", [nodeName, childNodeName]).click(); - cy.umbracoButtonByLabelKey('buttons_saveAndPublish').click(); - //Pop-up with what cultures you want to publish shows, click it - cy.get('.umb-list').should('be.visible'); - cy.get('.checkbox').last().click(); - cy.umbracoButtonByLabelKey('buttons_saveAndPublish').last().click(); - - // Close success notifications - cy.get('.alert-success > .close').click({multiple : true}); - // Publish Grandchild - cy.umbracoTreeItem("content", [nodeName, childNodeName, grandChildNodeName]).click(); - cy.umbracoButtonByLabelKey('buttons_saveAndPublish').click(); - // Pop-up with what cultures you want to publish shows, click it - cy.get('.umb-list').should('be.visible'); - cy.get('.checkbox').last().click(); - cy.umbracoButtonByLabelKey('buttons_saveAndPublish').last().click(); - - // Assert - cy.get('.alert-success').should('have.length', 2); - cy.get('.alert-warning').should('not.exist'); - }); - - it('Root node published in language A, Child node published in language A + B, Grandchild published in A + B + C', () => { - - const rootDocType = new DocumentTypeBuilder() - .withName(rootDocTypeName) - .withAllowAsRoot(true) - .withAllowCultureVariation(true) - .build(); - - saveNewLanguages(); - - cy.saveDocumentType(rootDocType).then((generatedRootDocType) => { - const rootContentNode = new ContentBuilder() - .withContentTypeAlias(generatedRootDocType["alias"]) - .withAction("publishNew") - .addVariant() - .withCulture('en-US') - .withName(nodeName) - .withSave(true) - .withPublish(true) - .done() - .build(); - - cy.saveContent(rootContentNode).then((generatedRootContent) => { - - configureDomain(generatedRootContent["id"], "/en", 1); - const childContentNode = new ContentBuilder() - .withContentTypeAlias(generatedRootDocType["alias"]) - .withAction("saveNew") - .withParent(generatedRootContent["id"]) - .addVariant() - .withCulture('en-US') - .withName(childNodeName) - .withSave(true) - .done() - .addVariant() - .withCulture(swedishCulture) - .withName("Barn") - .withSave(true) - .done() - .build(); - - cy.saveContent(childContentNode).then((generatedChildContent) => { - - configureDomain(generatedChildContent["id"], "/sv", swedishLanguageId); - const grandChildContentNode = new ContentBuilder() - .withContentTypeAlias(generatedRootDocType["alias"]) - .withAction("saveNew") - .withParent(generatedChildContent["id"]) - .addVariant() - .withCulture('en-US') - .withName(grandChildNodeName) - .withSave(true) - .done() - .addVariant() - .withCulture(swedishCulture) - .withName("Barnbarn") - .withSave(true) - .done() - .addVariant() - .withCulture(danishCulture) - .withName("Barnebarn") - .withSave(true) - .done() - .build(); - - cy.saveContent(grandChildContentNode); - }); - }); - }); - - // Refresh to update the tree - refreshContentTree(); - - // Publish Child - cy.umbracoTreeItem("content", [nodeName, childNodeName]).click(); - cy.umbracoButtonByLabelKey('buttons_saveAndPublish').click(); - // Pop-up with what cultures you want to publish shows, click it - cy.get('.umb-list').should('be.visible'); - cy.get('.checkbox').last().click(); - cy.umbracoButtonByLabelKey('buttons_saveAndPublish').last().click(); - - // Publish Grandchild - cy.umbracoTreeItem("content", [nodeName, childNodeName, grandChildNodeName]).click(); - cy.umbracoButtonByLabelKey('buttons_saveAndPublish').click(); - // Pop-up with what cultures you want to publish shows, click it - cy.get('.umb-list').should('be.visible'); - cy.get('.checkbox').click({multiple : true}); - cy.umbracoButtonByLabelKey('buttons_saveAndPublish').last().click(); - - // Assert - cy.get('.alert-success').should('exist'); - cy.get('.alert-warning').should('exist'); - }); -}); diff --git a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Content/urlpicker.ts b/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Content/urlpicker.ts deleted file mode 100644 index c8db6380ac..0000000000 --- a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Content/urlpicker.ts +++ /dev/null @@ -1,72 +0,0 @@ -/// -import { - DocumentTypeBuilder, - ContentBuilder, - AliasHelper -} from 'umbraco-cypress-testhelpers'; - -context('Url Picker', () => { - - beforeEach(() => { - cy.umbracoLogin(Cypress.env('username'), Cypress.env('password'), false); - }); - - it('Test Url Picker', () => { - - const urlPickerDocTypeName = 'Url Picker Test'; - const pickerDocTypeAlias = AliasHelper.toAlias(urlPickerDocTypeName); - cy.umbracoEnsureDocumentTypeNameNotExists(urlPickerDocTypeName); - cy.deleteAllContent(); - const pickerDocType = new DocumentTypeBuilder() - .withName(urlPickerDocTypeName) - .withAlias(pickerDocTypeAlias) - .withAllowAsRoot(true) - .withDefaultTemplate(pickerDocTypeAlias) - .addGroup() - .withName('ContentPickerGroup') - .addUrlPickerProperty() - .withAlias('picker') - .done() - .done() - .build(); - - cy.saveDocumentType(pickerDocType); - cy.editTemplate(urlPickerDocTypeName, '@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage' + - '\n@{' + - '\n Layout = null;' + - '\n}' + - '\n@foreach(var link in @Model.Picker)' + - '\n{' + - '\n @link.Name' + - '\n}'); - // Create content with url picker - cy.get('li .umb-tree-root:contains("Content")').should("be.visible").rightclick(); - cy.get('[data-element="action-create"]').click(); - cy.get('[data-element="action-create-' + pickerDocTypeAlias + '"] > .umb-action-link').click(); - // Fill out content - cy.umbracoEditorHeaderName('UrlPickerContent'); - cy.umbracoButtonByLabelKey('buttons_saveAndPublish').click(); - cy.umbracoSuccessNotification().should('be.visible'); - cy.get('.umb-node-preview-add').click(); - // Should really try and find a better way to do this, but umbracoTreeItem tries to click the content pane in the background - cy.get('#treePicker li:first', {timeout: 6000}).click(); - cy.get('.umb-editor-footer-content__right-side > [button-style="success"] > .umb-button > .btn > .umb-button__content').click(); - cy.get('.umb-node-preview__name').should('be.visible'); - //Save and publish - cy.umbracoButtonByLabelKey('buttons_saveAndPublish').click(); - cy.umbracoSuccessNotification().should('be.visible'); - //Waiting to ensure we have saved properly before leaving - cy.reload(); - //Assert - cy.get('.umb-notifications__notifications > .alert-error').should('not.exist'); - //Editing template with some content - - //Testing if the edits match the expected results - const expected = 'UrlPickerContent'; - cy.umbracoVerifyRenderedViewContent('/', expected, true).should('be.true'); - //clean - cy.umbracoEnsureDocumentTypeNameNotExists(urlPickerDocTypeName); - cy.umbracoEnsureTemplateNameNotExists(urlPickerDocTypeName); - - }); -}); diff --git a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Content/variantPermissions.ts b/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Content/variantPermissions.ts deleted file mode 100644 index 07b5dfb1e7..0000000000 --- a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Content/variantPermissions.ts +++ /dev/null @@ -1,169 +0,0 @@ -/// -import { - ContentBuilder, - DocumentTypeBuilder, - UserBuilder, - UserGroupBuilder, -} from 'umbraco-cypress-testhelpers'; - -context('Content', () => { - - beforeEach(() => { - cy.umbracoLogin(Cypress.env('username'), Cypress.env('password')); - }); - - function CreateOrUpdateUserGroup(name, id, alias, languages){ - const adminUserGroup = new UserGroupBuilder() - .withName(name) - .withId(id) - .withAlias(alias) - .withLanguages(languages || []) - .withSections(["content", "media", "settings", "packages", "users", "member", "forms", "translation"]) - .addDefaultPermissions() - .withAllowAll() - .done() - .withAction("save") - .withUsers([-1]) - .build(); - cy.saveUserGroup(adminUserGroup) - } - - it('Can publish language you have access to', () => { - const documentTypeName = "TestType"; - const groupName = "Test group"; - const nodeName = "Home"; - const danishNodeName = "Hjem"; - const language1 = 'da'; - const language2 = 'en-US'; - const email = "test@test.com"; - - - cy.umbracoEnsureUserEmailNotExists(email); - cy.umbracoEnsureUserGroupNameNotExists(groupName); - cy.umbracoEnsureLanguageCultureNotExists(language1); - cy.umbracoEnsureDocumentTypeNameNotExists(documentTypeName); - cy.deleteAllContent(); - - cy.umbracoCreateLanguage(language1, false, '1'); - - CreateOrUpdateUserGroup("Administrators", 1, "admin", [1]); - CreateOrUpdateUserGroup("Sensitive data", 5, "sensitiveData", [1]); - - const documentType = new DocumentTypeBuilder() - .withName(documentTypeName) - .withAllowAsRoot(true) - .withAllowCultureVariation(true) - .build(); - - cy.saveDocumentType(documentType).then((generatedDocumentType) => { - const rootContentNode = new ContentBuilder() - .withAction("saveNew") - .withContentTypeAlias(generatedDocumentType["alias"]) - .addVariant() - .withName(nodeName) - .withSave(true) - .withCulture(language2) - .done() - .addVariant() - .withName(danishNodeName) - .withSave(true) - .withCulture(language1) - .done() - .build(); - - cy.saveContent(rootContentNode) - }); - - cy.get('.umb-tree-root-link').rightclick(); - cy.umbracoContextMenuAction('action-refreshNode').click(); - // We have to wait in case the execution is slow, otherwise we'll try and click the item before it appears in the UI - cy.get('.umb-tree-item__inner').should('exist', {timeout: 10000}); - cy.umbracoTreeItem("content", [nodeName]).click(); - cy.umbracoButtonByLabelKey("buttons_saveAndPublish").click(); - cy.get('[data-element="overlay"]').get('[label-key="buttons_saveAndPublish"]').last().click(); - cy.umbracoSuccessNotification().should('be.visible'); - - // Clean up - // This is a bit ugly, but because we have messed with the admin privileges, we have to return them to the same state afterwards - - CreateOrUpdateUserGroup("Administrators", 1, "admin", []); - CreateOrUpdateUserGroup("Sensitive data", 5, "sensitiveData", []); - - cy.umbracoEnsureUserEmailNotExists(email); - cy.umbracoEnsureUserGroupNameNotExists(groupName); - cy.umbracoEnsureLanguageCultureNotExists(language1); - cy.umbracoEnsureDocumentTypeNameNotExists(documentTypeName); - cy.deleteAllContent(); - }); - - it('Can publish language you have access to', () => { - const documentTypeName = "TestType"; - const groupName = "Test group"; - const nodeName = "Home"; - const danishNodeName = "Hjem"; - const language1 = 'da'; - const language2 = 'en-US'; - const email = "test@test.com"; - - - cy.umbracoEnsureUserEmailNotExists(email); - cy.umbracoEnsureUserGroupNameNotExists(groupName); - cy.umbracoEnsureLanguageCultureNotExists(language1); - cy.umbracoEnsureDocumentTypeNameNotExists(documentTypeName); - cy.deleteAllContent(); - - cy.umbracoCreateLanguage(language1, false, '1'); - - CreateOrUpdateUserGroup("Administrators", 1, "admin", [1]); - CreateOrUpdateUserGroup("Sensitive data", 5, "sensitiveData", [1]); - - - const documentType = new DocumentTypeBuilder() - .withName(documentTypeName) - .withAllowAsRoot(true) - .withAllowCultureVariation(true) - .build(); - - cy.saveDocumentType(documentType).then((generatedDocumentType) => { - const rootContentNode = new ContentBuilder() - .withAction("saveNew") - .withContentTypeAlias(generatedDocumentType["alias"]) - .addVariant() - .withName(nodeName) - .withSave(true) - .withCulture(language2) - .done() - .addVariant() - .withName(danishNodeName) - .withSave(true) - .withCulture(language1) - .done() - .build(); - - cy.saveContent(rootContentNode) - }); - cy.reload(); - cy.get('.umb-tour-step', { timeout: 60000 }).should('be.visible'); // We now due to the api calls this will be shown, but slow computers can take a while - cy.get('.umb-tour-step__close').click(); - - cy.get('.umb-language-picker__toggle').click(); - cy.contains('Danish').click(); - cy.umbracoTreeItem("content", [danishNodeName]).click(); - cy.umbracoButtonByLabelKey("buttons_saveAndPublish").click(); - cy.get('[alias="overlaySubmit"] button').click({force: true}) - cy.get('[data-element="overlay"]').should('be.visible'); - - // Clean up - - // This is a bit ugly, but because we have messed with the admin privileges, we have to return them to the same state afterwards - CreateOrUpdateUserGroup("Administrators", 1, "admin", []); - CreateOrUpdateUserGroup("Sensitive data", 5, "sensitiveData", []); - - cy.umbracoEnsureUserEmailNotExists(email); - cy.umbracoEnsureUserGroupNameNotExists(groupName); - cy.umbracoEnsureLanguageCultureNotExists(language1); - cy.umbracoEnsureDocumentTypeNameNotExists(documentTypeName); - cy.deleteAllContent(); - }); - -}); \ No newline at end of file diff --git a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/DataTypes/dataTypes.ts b/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/DataTypes/dataTypes.ts deleted file mode 100644 index 6f1e8659fe..0000000000 --- a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/DataTypes/dataTypes.ts +++ /dev/null @@ -1,143 +0,0 @@ -/// -import { - AliasHelper, - ApprovedColorPickerDataTypeBuilder, - TextBoxDataTypeBuilder, -} from 'umbraco-cypress-testhelpers'; - -context('DataTypes', () => { - - beforeEach(() => { - cy.umbracoLogin(Cypress.env('username'), Cypress.env('password')); - }); - - it('Tests Approved Colors', () => { - cy.deleteAllContent(); - const name = 'Approved Colour Test'; - const alias = AliasHelper.toAlias(name); - - cy.umbracoEnsureDocumentTypeNameNotExists(name); - cy.umbracoEnsureDataTypeNameNotExists(name); - - const pickerDataType = new ApprovedColorPickerDataTypeBuilder() - .withName(name) - .withPrevalues(['000000', 'FF0000']) - .build() - - //umbracoMakeDocTypeWithDataTypeAndContent(name, alias, pickerDataType); - cy.umbracoCreateDocTypeWithContent(name, alias, pickerDataType); - //Editing template with some content - cy.editTemplate(name, '@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage' + - '\n@{' + - '\n Layout = null;' + - '\n}' + - '\n

Lorem ipsum dolor sit amet

'); - - // Act - // Enter content - cy.umbracoRefreshContentTree(); - cy.umbracoTreeItem("content", [name]).click(); - //Pick a colour - cy.get('.btn-000000').click(); - //Save - cy.umbracoButtonByLabelKey('buttons_saveAndPublish').click(); - cy.umbracoSuccessNotification().should('be.visible'); - - //Assert - const expected = `

Lorem ipsum dolor sit amet

`; - cy.umbracoVerifyRenderedViewContent('/', expected, true).should('be.true'); - cy.get('.umb-button__overlay').should('not.be.visible'); - - //Pick another colour to verify both work - cy.get('.btn-FF0000').click(); - //Save - cy.umbracoButtonByLabelKey('buttons_saveAndPublish').click(); - cy.umbracoSuccessNotification().should('be.visible'); - cy.umbracoButtonByLabelKey('buttons_saveAndPublish').should('be.visible'); - cy.get('.umb-button__overlay').should('not.be.visible'); - //Assert - const expected2 = '

Lorem ipsum dolor sit amet

'; - cy.umbracoVerifyRenderedViewContent('/', expected2, true).should('be.true'); - - //Clean - cy.umbracoEnsureDataTypeNameNotExists(name); - cy.umbracoEnsureDocumentTypeNameNotExists(name); - cy.umbracoEnsureTemplateNameNotExists(name); - }); - - it('Tests Textbox Maxlength', () => { - cy.deleteAllContent(); - const name = 'Textbox Maxlength Test'; - const alias = AliasHelper.toAlias(name); - - cy.umbracoEnsureDocumentTypeNameNotExists(name); - cy.umbracoEnsureDataTypeNameNotExists(name); - - const textBoxDataType = new TextBoxDataTypeBuilder() - .withName(name) - .withMaxChars(10) - .build() - - cy.umbracoCreateDocTypeWithContent(name, alias, textBoxDataType); - - // Act - // Enter content - // Assert no helptext with (max-2) chars & can save - cy.umbracoRefreshContentTree(); - cy.umbracoTreeItem("content", [name]).click(); - cy.get('input[name="textbox"]').type('12345678'); - cy.get('localize[key="textbox_characters_left"]').should('not.exist'); - cy.umbracoButtonByLabelKey('buttons_saveAndPublish').click(); - cy.umbracoSuccessNotification().should('be.visible'); - cy.get('.property-error').should('not.be.visible'); - - // Add char and assert helptext appears - no publish to save time & has been asserted above & below - cy.get('input[name="textbox"]').type('9'); - cy.get('localize[key="textbox_characters_left"]').contains('characters left').should('exist'); - cy.get('.property-error').should('not.be.visible'); - - // Add char and assert errortext appears and can't save - cy.get('input[name="textbox"]').type('10'); // 1 char over max - cy.get('localize[key="textbox_characters_exceed"]').contains('too many').should('exist'); - cy.umbracoButtonByLabelKey('buttons_saveAndPublish').click(); - cy.get('.property-error').should('be.visible'); - - // Clean - cy.umbracoEnsureTemplateNameNotExists(name); - cy.umbracoEnsureDataTypeNameNotExists(name); - cy.umbracoEnsureDocumentTypeNameNotExists(name); - }) - - // it('Tests Checkbox List', () => { - // const name = 'CheckBox List'; - // const alias = AliasHelper.toAlias(name); - - // cy.umbracoEnsureDocumentTypeNameNotExists(name); - // cy.umbracoEnsureDataTypeNameNotExists(name); - - // const pickerDataType = new CheckBoxListDataTypeBuilder() - // .withName(name) - // .withPrevalues(['Choice 1', 'Choice 2']) - // .build() - - // cy.umbracoCreateDocTypeWithContent(name, alias, pickerDataType); - // // Act - // // Enter content - // cy.umbracoRefreshContentTree(); - // cy.umbracoTreeItem("content", [name]).click(); - // //Check box 1 - // cy.get(':nth-child(1) > umb-checkbox.ng-isolate-scope > .checkbox > .umb-form-check__symbol > .umb-form-check__state > .umb-form-check__check') - // .click(); - // //Save - // cy.umbracoButtonByLabelKey('buttons_saveAndPublish').click(); - // cy.umbracoSuccessNotification().should('be.visible'); - - // //Edit template with content - // cy.editTemplate(name, '@inherits Umbraco.Web.Mvc.UmbracoViewPage' + - // '\n@using ContentModels = Umbraco.Web.PublishedModels;' + - // '\n@{' + - // '\n Layout = null;' + - // '\n}' + - // '\n

@Model.UmbracoTest

'); - // }); -}); diff --git a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/HelpPanel/systemInformation.ts b/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/HelpPanel/systemInformation.ts deleted file mode 100644 index 8bed0bf7ce..0000000000 --- a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/HelpPanel/systemInformation.ts +++ /dev/null @@ -1,60 +0,0 @@ -/// - -function openSystemInformation(){ - //We have to wait for page to load, if the site is slow - cy.get('[data-element="global-help"]').should('be.visible').click(); - cy.get('.umb-help-list-item').last().should('be.visible').click(); - cy.get('.umb-drawer-content').scrollTo('bottom', {ensureScrollable : false}); -} - -context('System Information', () => { - - beforeEach(() => { - //arrange - cy.umbracoLogin(Cypress.env('username'), Cypress.env('password')); - cy.umbracoSetCurrentUserLanguage('en-US'); - }); - afterEach(() => { - cy.umbracoSetCurrentUserLanguage('en-US'); - }); - - it('Check System Info Displays', () => { - openSystemInformation(); - - // Assert - cy.get('.table').find('tr').should('contain', 'Server OS'); - cy.get('.table').find('tr').should('contain', 'Server Framework'); - cy.get('.table').find('tr').should('contain', 'Default Language'); - cy.get('.table').find('tr').should('contain', 'Umbraco Version'); - cy.get('.table').find('tr').should('contain', 'Current Culture'); - cy.get('.table').find('tr').should('contain', 'Current UI Culture'); - cy.get('.table').find('tr').should('contain', 'Current Webserver'); - cy.get('.table').find('tr').should('contain', 'Models Builder Mode'); - cy.get('.table').find('tr').should('contain', 'Debug Mode'); - cy.get('.table').find('tr').should('contain', 'Database Provider'); - cy.get('.table').find('tr').should('contain', 'Current Server Role'); - cy.get('.table').find('tr').should('contain', 'Browser'); - cy.get('.table').find('tr').should('contain', 'Browser OS'); - cy.contains('Default Language').parent().should('contain', 'en-US'); - cy.contains('Current Culture').parent().should('contain', 'en-US'); - cy.contains('Current UI Culture').parent().should('contain', 'en-US'); - }); - - it('Checks language displays correctly after switching', () => { - - //Navigate to edit user and change language - cy.umbracoGlobalUser().click(); - cy.get('[alias="editUser"]').click(); - cy.get('[name="culture"]').select('string:da-DK', { force: true}); - cy.umbracoButtonByLabelKey('buttons_save').click({force: true}); - cy.umbracoSuccessNotification().should('be.visible'); - - openSystemInformation(); - //Assert - cy.contains('Current Culture').parent().should('contain', 'da-DK'); - cy.contains('Current UI Culture').parent().should('contain', 'da-DK'); - cy.get('.umb-button__content').last().click(); - //Clean - cy.umbracoSetCurrentUserLanguage('en-US'); - }); -}); diff --git a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Languages/languages.ts b/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Languages/languages.ts deleted file mode 100644 index ceb747a7bc..0000000000 --- a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Languages/languages.ts +++ /dev/null @@ -1,63 +0,0 @@ -/// - -context('Languages', () => { - - beforeEach(() => { - cy.umbracoLogin(Cypress.env('username'), Cypress.env('password'), false); - }); - - it('Creates language', () => { - // Setup - const language = 'Danish'; - const culture = 'da'; - cy.umbracoEnsureLanguageCultureNotExists(culture); - cy.umbracoSection('settings'); - - cy.get('.umb-box-content').should('be.visible'); - cy.get('li .umb-tree-root:contains("Settings")').should("be.visible"); - // Enter language tree and create new language - cy.umbracoTreeItem('settings', ['Languages']).click(); - cy.umbracoButtonByLabelKey('languages_addLanguage').click(); - cy.get('select[name="newLang"]').select(language); - - // Save and assert success - cy.umbracoButtonByLabelKey('buttons_save').click(); - cy.umbracoSuccessNotification().should('be.visible'); - - // Cleanup - cy.umbracoEnsureLanguageCultureNotExists(culture); - }); - - it('Deletes language', () => { - // Setup - const language1 = 'da'; - const language2 = 'en-GB'; - cy.umbracoEnsureLanguageCultureNotExists(language1); - cy.umbracoEnsureLanguageCultureNotExists(language2); - - cy.umbracoCreateLanguage(language1, true, '1'); - cy.umbracoCreateLanguage(language2, true, '1'); - //Enter settings section and wait for everything to load - cy.umbracoSection('settings'); - cy.get('.umb-box-content').should('be.visible'); - cy.get('li .umb-tree-root:contains("Settings")').should("be.visible"); - - // Enter language tree and select the language we just created - cy.umbracoTreeItem('settings', ['Languages']).click(); - - // Assert there are 3 languages - cy.get('tbody > tr').should('have.length', 3); - - // Delete UK Language - cy.get('umb-button[label-key="general_delete"]').last().click(); - cy.umbracoButtonByLabelKey('contentTypeEditor_yesDelete').click(); - - // Assert there is only 2 languages - cy.get('tbody > tr').should('have.length', 2); - - // Cleanup - cy.umbracoEnsureLanguageCultureNotExists(language1); - cy.umbracoEnsureLanguageCultureNotExists(language2); - }); - -}); diff --git a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Login/login.ts b/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Login/login.ts deleted file mode 100644 index 4c7720d71f..0000000000 --- a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Login/login.ts +++ /dev/null @@ -1,62 +0,0 @@ -/// -context('Login', () => { - - beforeEach(() => { - cy.visit('/umbraco'); - }); - - it('Login with correct username and password', () => { - const username = Cypress.env('username'); - const password = Cypress.env('password'); - //Precondition - cy.get('.text-error').should('not.exist'); - - //Action - cy.get('#umb-username').type(username); - cy.get('#umb-passwordTwo').type(password); - cy.get('[label-key="general_login"]').click(); - - //Assert - cy.url().should('include', '/umbraco#/content') - cy.get('#umb-username').should('not.exist'); - cy.get('#umb-passwordTwo').should('not.exist'); - }); - - - it('Login with correct username but wrong password', () => { - const username = Cypress.env('username'); - const password = 'wrong'; - - //Precondition - cy.get('.text-error').should('not.exist'); - - //Action - cy.get('#umb-username').type(username); - cy.get('#umb-passwordTwo').type(password); - cy.get('[label-key="general_login"]').click(); - - //Assert - cy.get('.text-error').should('exist'); - cy.get('#umb-username').should('exist'); - cy.get('#umb-passwordTwo').should('exist'); - }); - - it('Login with wrong username and wrong password', () => { - const username = 'wrong-username'; - const password = 'wrong'; - - //Precondition - cy.get('.text-error').should('not.exist'); - - //Action - cy.get('#umb-username').type(username); - cy.get('#umb-passwordTwo').type(password); - cy.get('[label-key="general_login"]').click(); - - //Assert - cy.get('.text-error').should('exist'); - cy.get('#umb-username').should('exist'); - cy.get('#umb-passwordTwo').should('exist'); - }); - - }); \ No newline at end of file diff --git a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Media/media.ts b/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Media/media.ts deleted file mode 100644 index fd2f0a0972..0000000000 --- a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Media/media.ts +++ /dev/null @@ -1,195 +0,0 @@ -/// - -import {MediaBuilder} from 'umbraco-cypress-testhelpers'; - -context('Media', () => { - - beforeEach(() => { - cy.umbracoLogin(Cypress.env('username'), Cypress.env('password')); - cy.umbracoSection("media"); - }); - - function refreshMediaTree() { - // Refresh to update the tree - cy.get('li .umb-tree-root:contains("Media")').should("be.visible").rightclick(); - //Needs to wait or it can give an error - cy.wait(1000); - cy.get(".umb-outline").contains("Reload").click(); - } - - it('Create folder', () => { - const folderName = 'Media Folder'; - //Ensures that there is not already an existing folder with the same name - cy.umbracoEnsureMediaNameNotExists(folderName); - - //Action - //Creates folder - cy.get(".dropdown-toggle").contains("Create").click({force: true}); - cy.get('[role="menuitem"]').contains("Folder").click({force: true}); - cy.get('[data-element="editor-name-field"]').type(folderName); - cy.umbracoButtonByLabelKey("buttons_save").click(); - - //Assert - cy.umbracoSuccessNotification().should("be.visible"); - - //Cleans up - cy.umbracoEnsureMediaNameNotExists(folderName); - }); - - it('Create folder inside of folder', () => { - const folderName = 'Folder'; - const insideFolderName = 'Folder in folder'; - //Ensures that there is not already existing folders with the same names - cy.umbracoEnsureMediaNameNotExists(folderName); - cy.umbracoEnsureMediaNameNotExists(insideFolderName); - - //Action - //Creates the first folder with an API call - const mediaFolder = new MediaBuilder() - .withName(folderName) - .withContentTypeAlias('Folder') - .build() - cy.saveMedia(mediaFolder, null); - //Creates second folder - refreshMediaTree(); - cy.umbracoTreeItem('media', [folderName]).click(); - cy.get(".dropdown-toggle").contains("Create").click({force: true}); - cy.get('[role="menuitem"]').contains("Folder").click({force: true}); - cy.get('[data-element="editor-name-field"]').type(insideFolderName); - cy.umbracoButtonByLabelKey("buttons_save").click(); - - //Assert - cy.umbracoSuccessNotification().should("be.visible"); - - //Cleans up - cy.umbracoEnsureMediaNameNotExists(folderName); - cy.umbracoEnsureMediaNameNotExists(insideFolderName); - }); - - it('Create image', () => { - const imageName = 'ImageTest'; - //Ensures that there is not already an existing image with the name - cy.umbracoEnsureMediaNameNotExists(imageName); - const umbracoFileValue = {"src": "Umbraco.png,"} - - //Action - const mediaImage = new MediaBuilder() - .withName(imageName) - .withContentTypeAlias('Image') - .addProperty() - .withAlias("umbracoFile") - .withValue(umbracoFileValue) - .done() - .build() - const blob = Cypress.Blob.base64StringToBlob("iVBORw0KGgoAAAANSUhEUgAAADcAAAAjCAYAAAAwnJLLAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGpSURBVFhH7ZRNq0FRFIbPbzD3A/wKSUkZmCgzAyUpkhhRyMT8TIwlEylDI2WgJMyMmJAB+SqS5OvVXjY599ad3eyt/dRpnbXW7rSf1upo+GKUnKwoOVlRcrKi5GRFycmKkpMVJScrSk5WhJDr9/uIRqPYbDa8Aux2O2QyGVitVjidTrTbbd55cLvdUKlUUCgUcDqdeNXIR+XYBev1OtxuNzweD1ar1auu6zrK5TK9j8dj+P1+LJdL6jOazSZisRj2+z2v/OajcuxitVoNk8kEwWDQIMdqh8OBcjbFcDiM0WhE+Xw+RyKRoPgXQqwlk3qX+0m320UymcTxeKQnHo/D4XDA5XIhn89jvV7zk0aEl2MrydbvOaVerwefz4fZbIbr9YpqtYp0Oo3L5UL9d4SWY2KRSITik1arhWKxyDNgOp0ilUq9VvgdYeWYUCgUwnA45JUHg8EA2WwW5/OZ8kajgVwuJ+bk2F/RZrPBbDZTZPl2u4XX64XFYoHJZIKmaRQ7nQ5JlEol2O12Oh8IBLBYLPjXjAgxuf9CycmKkpMVJScrSk5WvlgOuANsVZDROrcwfgAAAABJRU5ErkJggg=="); - const testFile = new File([blob], "test.jpg"); - cy.saveMedia(mediaImage, testFile); - refreshMediaTree(); - - //Assert - cy.get('[data-element="tree-item-ImageTest"]').should("be.visible"); - - //Cleans up - cy.umbracoEnsureMediaNameNotExists(imageName); - }); - - it('Create SVG', () => { - const svgName = 'svgTest'; - //Ensures that there is not already an existing SVG with the name - cy.umbracoEnsureMediaNameNotExists(svgName); - - //Action - const mediaSVG = new MediaBuilder() - .withName(svgName) - .withContentTypeAlias('umbracoMediaVectorGraphics') - .build() - cy.saveMedia(mediaSVG, null); - refreshMediaTree(); - - //Assert - cy.get('[data-element="tree-item-svgTest"]').should("be.visible"); - - //Cleans up - cy.umbracoEnsureMediaNameNotExists(svgName); - }); - - it('Create Audio', () => { - const audioName = 'audioTest'; - //Ensures that there is not already an existing audio with the name - cy.umbracoEnsureMediaNameNotExists(audioName); - - //Action - const mediaAudio = new MediaBuilder() - .withName(audioName) - .withContentTypeAlias('umbracoMediaAudio') - .build() - cy.saveMedia(mediaAudio, null); - refreshMediaTree(); - - //Assert - cy.get('[data-element="tree-item-audioTest"]').should("be.visible"); - - //Cleans up - cy.umbracoEnsureMediaNameNotExists(audioName); - }); - - it('Create File', () => { - const fileName = 'fileTest'; - //Ensures that there is not already an existing file with the name - cy.umbracoEnsureMediaNameNotExists(fileName); - - //Action - const mediaFile = new MediaBuilder() - .withName(fileName) - .withContentTypeAlias('File') - .build() - cy.saveMedia(mediaFile, null); - refreshMediaTree(); - - //Assert - cy.get('[data-element="tree-item-fileTest"]').should("be.visible"); - - //Cleans up - cy.umbracoEnsureMediaNameNotExists(fileName); - }); - - it('Create Video', () => { - const videoName = 'videoTest'; - //Ensures that there is not already an existing video with the name - cy.umbracoEnsureMediaNameNotExists(videoName); - - //Action - const mediaVideo = new MediaBuilder() - .withName(videoName) - .withContentTypeAlias('umbracoMediaVideo') - .build() - cy.saveMedia(mediaVideo, null); - refreshMediaTree(); - - //Assert - cy.get('[data-element="tree-item-videoTest"]').should("be.visible"); - - //Cleans up - cy.umbracoEnsureMediaNameNotExists(videoName); - }); - - it('Create Article', () => { - const articleName = 'articleTest'; - //Ensures that there is not already an existing article with the name - cy.umbracoEnsureMediaNameNotExists(articleName); - - //Action - const mediaArticle = new MediaBuilder() - .withName(articleName) - .withContentTypeAlias('umbracoMediaArticle') - .build() - cy.saveMedia(mediaArticle, null); - refreshMediaTree(); - - //Assert - cy.get('[data-element="tree-item-articleTest"]').should("be.visible"); - - //Cleans up - cy.umbracoEnsureMediaNameNotExists(articleName); - }); -}); \ No newline at end of file diff --git a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Members/memberGroups.js b/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Members/memberGroups.js deleted file mode 100644 index 6add16b4ee..0000000000 --- a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Members/memberGroups.js +++ /dev/null @@ -1,32 +0,0 @@ -context('Member Groups', () => { - - beforeEach(() => { - cy.umbracoLogin(Cypress.env('username'), Cypress.env('password')); - }); - - it('Create member group', () => { - const name = "Test Group"; - - cy.umbracoEnsureMemberGroupNameNotExists(name); - - cy.umbracoSection('member'); - cy.get('li .umb-tree-root:contains("Members")').should("be.visible"); - - cy.umbracoTreeItem("member", ["Member Groups"]).rightclick(); - - cy.umbracoContextMenuAction("action-create").click(); - - //Type name - cy.umbracoEditorHeaderName(name); - - // Save - cy.get('.btn-success').click(); - - //Assert - cy.umbracoSuccessNotification().should('be.visible'); - - //Clean up - cy.umbracoEnsureMemberGroupNameNotExists(name); - }); - -}); diff --git a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Members/members.js b/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Members/members.js deleted file mode 100644 index 7317e66864..0000000000 --- a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Members/members.js +++ /dev/null @@ -1,44 +0,0 @@ -/// -context('Members', () => { - - beforeEach(() => { - cy.umbracoLogin(Cypress.env('username'), Cypress.env('password')); - }); - - it('Create member', () => { - const name = "Alice Bobson"; - const email = "alice-bobson@acceptancetest.umbraco"; - const password = "$AUlkoF*St0kgPiyyVEk5iU5JWdN*F7&"; - const passwordTimeout = 20000 - - cy.umbracoEnsureMemberEmailNotExists(email); - cy.umbracoSection('member'); - cy.get('li .umb-tree-root:contains("Members")').should("be.visible"); - - cy.umbracoTreeItem("member", ["Members"]).rightclick(); - - cy.umbracoContextMenuAction("action-create").click(); - cy.get('.menu-label').first().click(); - - //Type name - cy.umbracoEditorHeaderName(name); - - cy.get('[data-element="sub-view-umbMembership"]').click(); - - cy.get('input#_umb_login').clear().type(email); - cy.get('input#_umb_email').clear().type(email); - cy.get('input#password').clear().type(password, { timeout: passwordTimeout }); - cy.get('input#confirmPassword').clear().type(password, { timeout: passwordTimeout }); - - // Save - cy.get('.btn-success').click(); - - //Assert - cy.umbracoSuccessNotification().should('be.visible'); - - //Clean up - cy.umbracoEnsureMemberEmailNotExists(email); - - }); - -}); diff --git a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Packages/packages.ts b/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Packages/packages.ts deleted file mode 100644 index a83bbcd68f..0000000000 --- a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Packages/packages.ts +++ /dev/null @@ -1,162 +0,0 @@ -/// -import { - ContentBuilder, - DocumentTypeBuilder - } from 'umbraco-cypress-testhelpers'; - -context('Packages', () => { - const packageName = "TestPackage"; - const rootDocTypeName = "Test document type"; - const nodeName = "1) Home"; - - beforeEach(() => { - cy.umbracoLogin(Cypress.env('username'), Cypress.env('password'), false); - }); - - function CreatePackage(contentId){ - const newPackage = { - id: 0, - packageGuid: "00000000-0000-0000-0000-000000000000", - name: "TestPackage", - packagePath: "", - contentLoadChildNodes: false, - contentNodeId: contentId, - macros: [], - languages: [], - dictionaryItems: [], - templates: [], - partialViews: [], - documentTypes: [], - mediaTypes: [], - stylesheets: [], - scripts: [], - dataTypes: [], - mediaUdis: [], - mediaLoadChildNodes: false - } - const url = "/umbraco/backoffice/umbracoapi/package/PostSavePackage"; - cy.umbracoApiRequest(url, 'POST', newPackage); - } - - function CreateSimplePackage(){ - - const rootDocType = new DocumentTypeBuilder() - .withName(rootDocTypeName) - .withAllowAsRoot(true) - .build(); - - cy.saveDocumentType(rootDocType).then((generatedRootDocType) => { - const rootDocTypeAlias = generatedRootDocType["alias"]; - - const rootContentNode = new ContentBuilder() - .withContentTypeAlias(rootDocTypeAlias) - .withAction("saveNew") - .addVariant() - .withName(nodeName) - .withSave(true) - .done() - .build(); - cy.saveContent(rootContentNode).then((generatedContent) => { - CreatePackage(generatedContent.Id); - }); - }); - } - - it('Creates a simple package', () => { - - cy.umbracoEnsurePackageNameNotExists(packageName); - cy.deleteAllContent(); - cy.umbracoEnsureDocumentTypeNameNotExists(rootDocTypeName); - - const rootDocType = new DocumentTypeBuilder() - .withName(rootDocTypeName) - .withAllowAsRoot(true) - .build(); - - cy.saveDocumentType(rootDocType).then((generatedRootDocType) => { - const rootDocTypeAlias = generatedRootDocType["alias"]; - - const rootContentNode = new ContentBuilder() - .withContentTypeAlias(rootDocTypeAlias) - .withAction("saveNew") - .addVariant() - .withName(nodeName) - .withSave(true) - .done() - .build(); - cy.saveContent(rootContentNode); - }); - - // Navigate to create package section - cy.umbracoSection('packages'); - cy.contains('Created').click(); - cy.contains('Create package').click(); - - // Fill out package creation form - cy.get('#headerName').should('be.visible'); - cy.wait(1000); - cy.get('#headerName').type(packageName); - cy.get('.controls > .umb-node-preview-add').click(); - cy.get('.umb-tree-item__label').first().click(); - cy.contains('Create').click(); - - // Navigate pack to packages and Assert the file is created - cy.umbracoSection('packages'); - cy.contains('Created').click(); - cy.contains(packageName).should('be.visible'); - - // Cleanup - cy.umbracoEnsurePackageNameNotExists(packageName); - cy.deleteAllContent(); - cy.umbracoEnsureDocumentTypeNameNotExists(rootDocTypeName); - }); - - it('Delete package', () => { - - // Ensure cleanup before test - cy.deleteAllContent(); - cy.umbracoEnsureDocumentTypeNameNotExists(rootDocTypeName); - cy.umbracoEnsurePackageNameNotExists(packageName); - - CreateSimplePackage(); - - // Navigate to create package section - cy.umbracoSection('packages'); - cy.contains('Created').click(); - cy.contains('Delete').click(); - cy.wait(100); - cy.contains('Yes, delete').click(); - - // Assert - cy.contains('TestPackage').should('not.exist'); - - // Cleanup - cy.deleteAllContent(); - cy.umbracoEnsureDocumentTypeNameNotExists(rootDocTypeName); - cy.umbracoEnsurePackageNameNotExists(packageName); - }); - - it('Download package', () => { - - // Ensure cleanup before test - cy.deleteAllContent(); - cy.umbracoEnsureDocumentTypeNameNotExists(rootDocTypeName); - cy.umbracoEnsurePackageNameNotExists(packageName); - - CreateSimplePackage(); - - // Navigate to package and download - cy.umbracoSection('packages'); - cy.contains('Created').click(); - cy.contains('TestPackage').click(); - cy.contains('Download').click(); - - // Assert - cy.verifyDownload('package.xml'); - - // Cleanup - cy.deleteAllContent(); - cy.umbracoEnsureDocumentTypeNameNotExists(rootDocTypeName); - cy.umbracoEnsurePackageNameNotExists(packageName); - }); -}); diff --git a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/dataTypes.ts b/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/dataTypes.ts deleted file mode 100644 index e747058203..0000000000 --- a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/dataTypes.ts +++ /dev/null @@ -1,66 +0,0 @@ -/// -import {LabelDataTypeBuilder} from 'umbraco-cypress-testhelpers'; -context('Data Types', () => { - - beforeEach(() => { - cy.umbracoLogin(Cypress.env('username'), Cypress.env('password')); - }); - - it('Create data type', () => { - const name = "Test data type"; - - cy.umbracoEnsureDataTypeNameNotExists(name); - - cy.umbracoSection('settings'); - cy.get('li .umb-tree-root:contains("Settings")').should("be.visible"); - - cy.umbracoTreeItem("settings", ["Data Types"]).rightclick(); - - cy.umbracoContextMenuAction("action-create").click(); - cy.umbracoContextMenuAction("action-data-type").click(); - - //Type name - cy.umbracoEditorHeaderName(name); - - - cy.umbracoButtonByLabelKey('propertyEditorPicker_openPropertyEditorPicker').click(); - - cy.get('[title="Date/Time"]').click(); - - //Save - cy.get('.btn-success').click(); - - //Assert - cy.umbracoSuccessNotification().should('be.visible'); - - //Clean up - cy.umbracoEnsureDataTypeNameNotExists(name); - }); - - it('Delete data type', () => { - const name = "Test data type"; - cy.umbracoEnsureDataTypeNameNotExists(name); - - const dataType = new LabelDataTypeBuilder() - .withSaveNewAction() - .withName(name) - .build(); - - cy.saveDataType(dataType); - - cy.umbracoSection('settings'); - cy.get('li .umb-tree-root:contains("Settings")').should("be.visible"); - - cy.umbracoTreeItem("settings", ["Data Types", name]).rightclick(); - - cy.umbracoContextMenuAction("action-delete").click(); - - cy.umbracoButtonByLabelKey("general_delete").click(); - - cy.contains(name).should('not.exist'); - - cy.umbracoEnsureDataTypeNameNotExists(name); - - - }); -}); diff --git a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/documentTypes.ts b/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/documentTypes.ts deleted file mode 100644 index fa4c6ec9a3..0000000000 --- a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/documentTypes.ts +++ /dev/null @@ -1,81 +0,0 @@ -/// -import { DocumentTypeBuilder } from 'umbraco-cypress-testhelpers'; -context('Document Types', () => { - - beforeEach(() => { - cy.umbracoLogin(Cypress.env('username'), Cypress.env('password')); - }); - - it('Create document type', () => { - const name = "Test document type"; - - cy.umbracoEnsureDocumentTypeNameNotExists(name); - cy.umbracoEnsureTemplateNameNotExists(name); - - cy.umbracoSection('settings'); - cy.get('li .umb-tree-root:contains("Settings")').should("be.visible"); - - cy.umbracoTreeItem("settings", ["Document Types"]).rightclick(); - - cy.umbracoContextMenuAction("action-create").click(); - cy.umbracoContextMenuAction("action-documentType").click(); - //Type name - cy.umbracoEditorHeaderName(name); - - - cy.get('[data-element="group-add"]').click(); - - - cy.get('.umb-group-builder__group-title-input').type('Group name'); - cy.get('[data-element="property-add"]').click(); - cy.get('.editor-label').type('property name'); - cy.get('[data-element="editor-add"]').click(); - - //Search for textstring - cy.get('#datatype-search').type('Textstring'); - - // Choose first item - cy.get('ul.umb-card-grid [title="Textstring"]').closest("li").click(); - - // Save property - cy.get('.btn-success').last().click(); - - //Save - cy.get('.btn-success').click(); - - //Assert - cy.umbracoSuccessNotification().should('be.visible'); - cy.umbracoEnsureTemplateNameNotExists(name); - - //Clean up - cy.umbracoEnsureDocumentTypeNameNotExists(name); - }); - - it('Delete document type', () => { - const name = "Test document type"; - cy.umbracoEnsureDocumentTypeNameNotExists(name); - - const dataType = new DocumentTypeBuilder() - .withName(name) - .build(); - - cy.saveDocumentType(dataType); - - - cy.umbracoSection('settings'); - cy.get('li .umb-tree-root:contains("Settings")').should("be.visible"); - - cy.umbracoTreeItem("settings", ["Document Types", name]).rightclick(); - - cy.umbracoContextMenuAction("action-delete").click(); - - cy.get('label.checkbox').click(); - cy.umbracoButtonByLabelKey("delete").click(); - - cy.contains(name).should('not.exist'); - - cy.umbracoEnsureDocumentTypeNameNotExists(name); - - - }); -}); diff --git a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/languages.ts b/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/languages.ts deleted file mode 100644 index 336e5793d9..0000000000 --- a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/languages.ts +++ /dev/null @@ -1,35 +0,0 @@ -/// -context('Languages', () => { - - beforeEach(() => { - cy.umbracoLogin(Cypress.env('username'), Cypress.env('password')); - }); - - it('Add language', () => { - // For some reason the languages to chose fom seems to be translated differently than normal, as an example: - // My system is set to EN (US), but most languages are translated into Danish for some reason - // Aghem seems untranslated though? - const name = "Aghem"; // Must be an option in the select box - - cy.umbracoEnsureLanguageNameNotExists(name); - - cy.umbracoSection('settings'); - cy.get('li .umb-tree-root:contains("Settings")').should("be.visible"); - - cy.umbracoTreeItem("settings", ["Languages"]).click(); - - cy.umbracoButtonByLabelKey("languages_addLanguage").click(); - - cy.get('select[name="newLang"]').select(name); - - // //Save - cy.get('.btn-success').click(); - - //Assert - cy.umbracoSuccessNotification().should('be.visible'); - - //Clean up - cy.umbracoEnsureLanguageNameNotExists(name); - }); - -}); diff --git a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/macros.ts b/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/macros.ts deleted file mode 100644 index 40b5d5483d..0000000000 --- a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/macros.ts +++ /dev/null @@ -1,33 +0,0 @@ -/// -context('Macros', () => { - - beforeEach(() => { - cy.umbracoLogin(Cypress.env('username'), Cypress.env('password')); - }); - - it('Create macro', () => { - const name = "Test macro"; - - cy.umbracoEnsureMacroNameNotExists(name); - - cy.umbracoSection('settings'); - cy.get('li .umb-tree-root:contains("Settings")').should("be.visible"); - - cy.umbracoTreeItem("settings", ["Macros"]).rightclick(); - - cy.umbracoContextMenuAction("action-create").click(); - - cy.get('form[name="createMacroForm"]').within(($form) => { - cy.get('input[name="itemKey"]').type(name); - cy.get(".btn-primary").click(); - }); - - cy.location().should((loc) => { - expect(loc.hash).to.include('#/settings/macros/edit/') - }); - - //Clean up - cy.umbracoEnsureMacroNameNotExists(name); - }); - -}); diff --git a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/mediaTypes.ts b/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/mediaTypes.ts deleted file mode 100644 index 3556e757f5..0000000000 --- a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/mediaTypes.ts +++ /dev/null @@ -1,52 +0,0 @@ -/// -context('Media Types', () => { - - beforeEach(() => { - cy.umbracoLogin(Cypress.env('username'), Cypress.env('password')); - }); - - it('Create media type', () => { - const name = "Test media type"; - - cy.umbracoEnsureMediaTypeNameNotExists(name); - - cy.umbracoSection('settings'); - cy.get('li .umb-tree-root:contains("Settings")').should("be.visible"); - - cy.umbracoTreeItem("settings", ["Media Types"]).rightclick(); - - cy.umbracoContextMenuAction("action-create").click(); - cy.get('.menu-label localize[key="content_mediatype"]').click(); - - - //Type name - cy.umbracoEditorHeaderName(name); - - - cy.get('[data-element="group-add"]').click(); - - cy.get('.umb-group-builder__group-title-input').type('Group name'); - cy.get('[data-element="property-add"]').click(); - cy.get('.editor-label').type('property name'); - cy.get('[data-element="editor-add"]').click(); - - //Search for textstring - cy.get('#datatype-search').type('Textstring'); - - // Choose first item - cy.get('ul.umb-card-grid [title="Textstring"]').closest("li").click(); - - // Save property - cy.get('.btn-success').last().click(); - - //Save - cy.get('.btn-success').click(); - - //Assert - cy.umbracoSuccessNotification().should('be.visible'); - - //Clean up - cy.umbracoEnsureMediaTypeNameNotExists(name); - }); - -}); diff --git a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/memberTypes.ts b/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/memberTypes.ts deleted file mode 100644 index 4dcc80432e..0000000000 --- a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/memberTypes.ts +++ /dev/null @@ -1,50 +0,0 @@ -/// -context('Member Types', () => { - - beforeEach(() => { - cy.umbracoLogin(Cypress.env('username'), Cypress.env('password')); - }); - - it('Create member type', () => { - const name = "Test member type"; - - cy.umbracoEnsureMemberTypeNameNotExists(name); - - cy.umbracoSection('settings'); - cy.get('li .umb-tree-root:contains("Settings")').should("be.visible"); - - cy.umbracoTreeItem("settings", ["Member Types"]).rightclick(); - - cy.umbracoContextMenuAction("action-create").click(); - - //Type name - cy.umbracoEditorHeaderName(name); - - - cy.get('[data-element="group-add"]').click(); - - cy.get('.umb-group-builder__group-title-input').type('Group name'); - cy.get('[data-element="property-add"]').click(); - cy.get('.editor-label').type('property name'); - cy.get('[data-element="editor-add"]').click(); - - //Search for textstring - cy.get('#datatype-search').type('Textstring'); - - // Choose first item - cy.get('ul.umb-card-grid [title="Textstring"]').closest("li").click(); - - // Save property - cy.get('.btn-success').last().click(); - - //Save - cy.get('.btn-success').click(); - - //Assert - cy.umbracoSuccessNotification().should('be.visible'); - - //Clean up - cy.umbracoEnsureMemberTypeNameNotExists(name); - }); - -}); diff --git a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/partialsViewMacroFiles.ts b/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/partialsViewMacroFiles.ts deleted file mode 100644 index f75dcee89e..0000000000 --- a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/partialsViewMacroFiles.ts +++ /dev/null @@ -1,155 +0,0 @@ -/// -import { PartialViewMacroBuilder } from "umbraco-cypress-testhelpers"; - -context('Partial View Macro Files', () => { - - beforeEach(() => { - cy.umbracoLogin(Cypress.env('username'), Cypress.env('password')); - }); - - function openPartialViewMacroCreatePanel() { - cy.umbracoSection('settings'); - cy.get('li .umb-tree-root:contains("Settings")').should("be.visible"); - - cy.umbracoTreeItem("settings", ["Partial View Macro Files"]).rightclick(); - cy.umbracoContextMenuAction("action-create").click(); - } - - function cleanup(name, extension = ".cshtml") { - const fileName = name + extension; - - cy.umbracoEnsureMacroNameNotExists(name); - cy.umbracoEnsurePartialViewMacroFileNameNotExists(fileName); - } - - it('Create new partial view macro', () => { - const name = "TestPartialViewMacro"; - - cleanup(name); - - openPartialViewMacroCreatePanel(); - - cy.get('.menu-label localize[key="create_newPartialViewMacro"]').click(); - - //Type name - cy.umbracoEditorHeaderName(name); - - //Save - cy.get('.btn-success').click(); - - //Assert - cy.umbracoSuccessNotification().should('be.visible'); - cy.umbracoMacroExists(name).then(exists => { expect(exists).to.be.true; }); - - //Clean up - cleanup(name); - }); - - it('Create new partial view macro without macro', () => { - const name = "TestPartialMacrolessMacro"; - - cleanup(name); - - openPartialViewMacroCreatePanel(); - - cy.get('.menu-label').eq(1).click(); - - // Type name - cy.umbracoEditorHeaderName(name); - - // Save - cy.get('.btn-success').click(); - - // Assert - cy.umbracoSuccessNotification().should('be.visible'); - cy.umbracoMacroExists(name).then(exists => { expect(exists).to.be.false; }); - - // Clean - cleanup(name); - }); - - it('Create new partial view macro from snippet', () => { - const name = "TestPartialFromSnippet"; - - cleanup(name); - - openPartialViewMacroCreatePanel(); - - cy.get('.menu-label').eq(2).click(); - - // Select snippet - cy.get('.menu-label').eq(1).click(); - - // Type name - cy.umbracoEditorHeaderName(name); - - // Save - cy.get('.btn-success').click(); - - // Assert - cy.umbracoSuccessNotification().should('be.visible'); - cy.umbracoMacroExists(name).then(exists => { expect(exists).to.be.true; }); - - // Clean - cleanup(name); - }); - - it('Delete partial view macro', () => { - const name = "TestDeletePartialViewMacro"; - const fullName = name + ".cshtml" - - cleanup(name); - - const partialViewMacro = new PartialViewMacroBuilder() - .withName(name) - .withContent("@inherits Umbraco.Web.Macros.PartialViewMacroPage") - .build(); - - cy.savePartialViewMacro(partialViewMacro); - - // Navigate to settings - cy.umbracoSection('settings'); - cy.get('li .umb-tree-root:contains("Settings")').should("be.visible"); - - // Delete partialViewMacro - cy.umbracoTreeItem("settings", ["Partial View Macro Files", fullName]).rightclick(); - cy.umbracoContextMenuAction("action-delete").click(); - cy.umbracoButtonByLabelKey("general_ok").click(); - - // Assert - cy.contains(fullName).should('not.exist'); - - // Clean - cleanup(name); - }); - - it('Edit partial view macro', () => { - const name = "TestPartialViewMacroEditable"; - const fullName = name + ".cshtml"; - - cleanup(name); - - const partialViewMacro = new PartialViewMacroBuilder() - .withName(name) - .withContent("@inherits Umbraco.Web.Macros.PartialViewMacroPage") - .build(); - - cy.savePartialViewMacro(partialViewMacro); - - // Navigate to settings - cy.umbracoSection('settings'); - cy.get('li .umb-tree-root:contains("Settings")').should("be.visible"); - cy.umbracoTreeItem("settings", ["Partial View Macro Files", fullName]).click(); - - // Type an edit - cy.get('.ace_text-input').type(" // test", {force:true} ); - // Save - cy.get('.btn-success').click(); - - // Assert - cy.umbracoSuccessNotification().should('be.visible'); - - cleanup(name); - }); - -}); diff --git a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/partialsViews.ts b/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/partialsViews.ts deleted file mode 100644 index f388c371f9..0000000000 --- a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/partialsViews.ts +++ /dev/null @@ -1,145 +0,0 @@ -/// -import {PartialViewBuilder} from "umbraco-cypress-testhelpers"; - -context('Partial Views', () => { - - beforeEach(() => { - cy.umbracoLogin(Cypress.env('username'), Cypress.env('password')); - }); - - function navigateToSettings() { - cy.umbracoSection('settings'); - cy.get('li .umb-tree-root:contains("Settings")').should("be.visible"); - } - - function openPartialViewsCreatePanel() { - navigateToSettings(); - cy.umbracoTreeItem("settings", ["Partial Views"]).rightclick(); - } - - it('Create new empty partial view', () => { - const name = "TestPartialView"; - const fileName = name + ".cshtml"; - - cy.umbracoEnsurePartialViewNameNotExists(fileName); - - openPartialViewsCreatePanel(); - - cy.umbracoContextMenuAction("action-create").click(); - cy.get('.menu-label localize[key="create_newEmptyPartialView"]').click(); - - //Type name - cy.umbracoEditorHeaderName(name); - - //Save - cy.get('.btn-success').click(); - - //Assert - cy.umbracoSuccessNotification().should('be.visible'); - cy.umbracoPartialViewExists(fileName).then(exists => { expect(exists).to.be.true; }); - - //Clean up - cy.umbracoEnsurePartialViewNameNotExists(fileName); - }); - - it('Create partial view from snippet', () => { - const name = "TestPartialViewFromSnippet"; - const fileName = name + ".cshtml"; - - cy.umbracoEnsurePartialViewNameNotExists(fileName); - - openPartialViewsCreatePanel(); - - cy.umbracoContextMenuAction("action-create").click(); - cy.get('.menu-label').eq(1).click(); - // Select snippet - cy.get('.menu-label').eq(2).click(); - - // Type name - cy.umbracoEditorHeaderName(name); - - // Save - cy.get('.btn-success').click(); - - // Assert - cy.umbracoSuccessNotification().should('be.visible'); - cy.umbracoPartialViewExists(fileName).then(exists => { expect(exists).to.be.true; }); - - // Clean up - cy.umbracoEnsurePartialViewNameNotExists(fileName); - }); - - it('Partial view with no name', () => { - openPartialViewsCreatePanel(); - - cy.umbracoContextMenuAction("action-create").click(); - cy.get('.menu-label localize[key="create_newEmptyPartialView"]').click(); - - // The test would fail intermittently, most likely because the editor didn't have time to load - // This should ensure that the editor is loaded and the test should no longer fail unexpectedly. - cy.get('.ace_content', {timeout: 5000}).should('exist'); - - // Click save - cy.get('.btn-success').click(); - - // Asserts - cy.umbracoErrorNotification().should('be.visible'); - }); - - it('Delete partial view', () => { - const name = "TestDeletePartialView"; - const fileName = name + ".cshtml"; - - cy.umbracoEnsurePartialViewNameNotExists(fileName); - - // Build and save partial view - const partialView = new PartialViewBuilder() - .withName(name) - .withContent("@inherits UUmbraco.Cms.Web.Common.Views.UmbracoViewPage") - .build(); - - cy.savePartialView(partialView); - - navigateToSettings(); - - // Delete partial view - cy.umbracoTreeItem("settings", ["Partial Views", fileName]).rightclick(); - cy.umbracoContextMenuAction("action-delete").click(); - cy.umbracoButtonByLabelKey("general_ok").click(); - - // Assert - cy.contains(fileName).should('not.exist'); - cy.umbracoPartialViewExists(fileName).then(exists => { expect(exists).to.be.false; }); - - // Clean - cy.umbracoEnsurePartialViewNameNotExists(fileName); - }); - - it('Edit partial view', () => { - const name = 'EditPartialView'; - const fileName = name + ".cshtml"; - - cy.umbracoEnsurePartialViewNameNotExists(fileName); - - const partialView = new PartialViewBuilder() - .withName(name) - .withContent("@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage\n") - .build(); - - cy.savePartialView(partialView); - - navigateToSettings(); - // Open partial view - cy.umbracoTreeItem("settings", ["Partial Views", fileName]).click(); - // Edit - cy.get('.ace_text-input').type("var num = 5;", {force:true} ); - cy.get('.btn-success').click(); - - // Assert - cy.umbracoSuccessNotification().should('be.visible'); - // Clean - cy.umbracoEnsurePartialViewNameNotExists(fileName); - }); - - -}); diff --git a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/relationTypes.ts b/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/relationTypes.ts deleted file mode 100644 index 1b903d4b76..0000000000 --- a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/relationTypes.ts +++ /dev/null @@ -1,42 +0,0 @@ -/// -context('Relation Types', () => { - - beforeEach(() => { - cy.umbracoLogin(Cypress.env('username'), Cypress.env('password')); - }); - - it('Create relation type', () => { - const name = "Test relation type"; - - cy.umbracoEnsureRelationTypeNameNotExists(name); - - cy.umbracoSection('settings'); - cy.get('li .umb-tree-root:contains("Settings")').should("be.visible"); - - cy.umbracoTreeItem("settings", ["Relation Types"]).rightclick(); - - cy.umbracoContextMenuAction("action-create").click(); - - cy.get('form[name="createRelationTypeForm"]').within(($form) => { - cy.get('input[name="relationTypeName"]').type(name); - - cy.get('[name="relationType-direction"] input').first().click({force:true}); - - cy.get('select[name="relationType-parent"]').select('Document'); - - cy.get('select[name="relationType-child"]').select('Media'); - - cy.get('[name="relationType-isdependency"]').last().click({force: true}) - - cy.get(".btn-primary").click(); - }); - - cy.location().should((loc) => { - expect(loc.hash).to.include('#/settings/relationTypes/edit/') - }) - - //Clean up - cy.umbracoEnsureRelationTypeNameNotExists(name); - }); - -}); diff --git a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/scripts.ts b/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/scripts.ts deleted file mode 100644 index 0d3e15b56e..0000000000 --- a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/scripts.ts +++ /dev/null @@ -1,126 +0,0 @@ -/// -import { ScriptBuilder } from "umbraco-cypress-testhelpers"; - -context('Scripts', () => { - - beforeEach(() => { - cy.umbracoLogin(Cypress.env('username'), Cypress.env('password')); - }); - - function navigateToSettings() { - cy.umbracoSection('settings'); - cy.get('li .umb-tree-root:contains("Settings")').should("be.visible"); - } - - it('Create new JavaScript file', () => { - const name = "TestScript"; - const fileName = name + ".js"; - - cy.umbracoEnsureScriptNameNotExists(fileName); - - navigateToSettings() - - cy.umbracoTreeItem("settings", ["Scripts"]).rightclick(); - - cy.umbracoContextMenuAction("action-create").click(); - cy.get('.menu-label localize[key="create_newJavascriptFile"]').click(); - //We have to wait here till everything is loaded, or worker will throw error - cy.intercept('/umbraco/lib/ace-builds/src-min-noconflict/worker-javascript.js').as('aceWorker'); - cy.wait('@aceWorker'); - - //Type name - cy.umbracoEditorHeaderName(name); - - //Save - cy.get('.btn-success').click(); - - //Assert - cy.umbracoSuccessNotification().should('be.visible'); - - - cy.umbracoScriptExists(fileName).should('be.true'); - - - //Clean up - cy.umbracoEnsureScriptNameNotExists(fileName); - }); - - it('Delete a JavaScript file', () => { - const name = "TestDeleteScriptFile"; - const fileName = name + ".js"; - - cy.umbracoEnsureScriptNameNotExists(fileName); - - const script = new ScriptBuilder() - .withName(name) - .withContent('alert("this is content");') - .build(); - - cy.saveScript(script); - - navigateToSettings() - - cy.umbracoTreeItem("settings", ["Scripts", fileName]).rightclick(); - cy.umbracoContextMenuAction("action-delete").click(); - cy.umbracoButtonByLabelKey("general_ok").click(); - - cy.contains(fileName).should('not.exist'); - cy.umbracoScriptExists(name).should('be.false'); - - cy.umbracoEnsureScriptNameNotExists(fileName); - }); - - it('Update JavaScript file', () => { - const name = "TestEditJavaScriptFile"; - const nameEdit = "Edited"; - let fileName = name + ".js"; - - const originalContent = 'console.log("A script);\n'; - const edit = 'alert("content");'; - const expected = originalContent + edit; - - cy.umbracoEnsureScriptNameNotExists(fileName); - - const script = new ScriptBuilder() - .withName(name) - .withContent(originalContent) - .build(); - cy.saveScript(script); - - navigateToSettings(); - cy.umbracoTreeItem("settings", ["Scripts", fileName]).click(); - - cy.get('.ace_text-input').type(edit, { force: true }); - - // Since scripts has no alias it should be safe to not use umbracoEditorHeaderName - // umbracoEditorHeaderName does not like {backspace} - cy.get('#headerName').type("{backspace}{backspace}{backspace}" + nameEdit).should('have.value', name+nameEdit); - fileName = name + nameEdit + ".js"; - cy.get('.btn-success').click(); - - cy.umbracoSuccessNotification().should('be.visible'); - cy.umbracoVerifyScriptContent(fileName, expected).should('be.true'); - - cy.umbracoEnsureScriptNameNotExists(fileName); - }); - - it('Can Delete folder', () => { - const folderName = "TestFolder"; - - // The way scripts and folders are fetched and deleted are identical - cy.umbracoEnsureScriptNameNotExists(folderName); - cy.saveFolder('scripts', folderName); - - navigateToSettings() - - cy.umbracoTreeItem("settings", ["Scripts", folderName]).rightclick(); - cy.umbracoContextMenuAction("action-delete").click(); - cy.umbracoButtonByLabelKey("general_ok").click(); - - cy.contains(folderName).should('not.exist'); - cy.umbracoScriptExists(folderName).should('be.false') - - // A script an a folder is the same thing in this case - cy.umbracoEnsureScriptNameNotExists(folderName); - }); -}); diff --git a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/stylesheets.ts b/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/stylesheets.ts deleted file mode 100644 index 3a748acc6f..0000000000 --- a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/stylesheets.ts +++ /dev/null @@ -1,74 +0,0 @@ -/// -context('Stylesheets', () => { - - const name = "TestStylesheet"; - const fileName = name + ".css"; - - beforeEach(() => { - cy.umbracoLogin(Cypress.env('username'), Cypress.env('password')); - - cy.umbracoEnsureStylesheetNameNotExists(fileName); - }); - - afterEach(() => { - // Clean up, this ensures that even if tests break we clean up - cy.umbracoEnsureStylesheetNameNotExists(fileName); - }); - - it('Create new style sheet file', () => { - - cy.umbracoSection('settings'); - cy.get('li .umb-tree-root:contains("Settings")').should("be.visible"); - - cy.umbracoTreeItem("settings", ["Stylesheets"]).rightclick(); - - cy.umbracoContextMenuAction("action-create").click(); - cy.get('.menu-label').first().click(); // TODO: Would be better to use something like cy.umbracoContextMenuAction("action-mediaType").click(); - // We have to wait here till everything is loaded, or worker will throw error - cy.intercept('/umbraco/lib/ace-builds/src-min-noconflict/worker-css.js').as('aceWorker'); - cy.wait('@aceWorker'); - - // Type name - cy.umbracoEditorHeaderName(name); - - // Save - cy.get('.btn-success').click(); - - // Assert - cy.umbracoSuccessNotification().should('be.visible'); - - }); - - it('Deletes a stylesheet', () => { - - var stylesheetData = { - "virtualPath": "/css/", - "path": null, - "name": name, - "content": "", - "fileType": "stylesheets", - "snippet": null, - "id": "0", - "notifications": [] - } - - let url = '/umbraco/backoffice/umbracoapi/codefile/PostSave' - cy.umbracoApiRequest(url, 'POST', stylesheetData); - - // Navigate to Settings section - cy.umbracoSection('settings'); - cy.get('li .umb-tree-root:contains("Settings")').should("be.visible"); - - // Open stylesheet tree - cy.get('[data-element="tree-item-stylesheets"] > .umb-tree-item__inner > .umb-tree-item__arrow').click(); - - // Delete stylesheet - cy.get('.umb-tree-item__inner > .umb-tree-item__label').contains(name).rightclick(); - cy.get('.umb-action-link').click(); - cy.get('[ng-if="showConfirm"]').click(); - - // Assert - cy.get('.umb-tree-item__inner > .umb-tree-item__label').contains(name).should('not.exist'); - - }); -}); diff --git a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/templates.ts b/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/templates.ts deleted file mode 100644 index bd037658da..0000000000 --- a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/templates.ts +++ /dev/null @@ -1,181 +0,0 @@ -/// -import {TemplateBuilder} from 'umbraco-cypress-testhelpers'; - -context('Templates', () => { - - beforeEach(() => { - cy.umbracoLogin(Cypress.env('username'), Cypress.env('password')); - }); - - function navigateToSettings() { - cy.umbracoSection('settings'); - cy.get('li .umb-tree-root:contains("Settings")').should("be.visible"); - } - - function createTemplate() { - navigateToSettings(); - cy.umbracoTreeItem("settings", ["Templates"]).rightclick(); - cy.umbracoContextMenuAction("action-create").click(); - } - - it('Create template', () => { - const name = "Create template test"; - cy.umbracoEnsureTemplateNameNotExists(name); - - createTemplate(); - // We have to wait for the ace editor to load, because when the editor is loading it will "steal" the focus briefly, - // which causes the save event to fire if we've added something to the header field, causing errors. - cy.wait(500); - - //Type name - cy.umbracoEditorHeaderName(name); - // Save - // We must drop focus for the auto save event to occur. - cy.get('.btn-success').focus(); - // And then wait for the auto save event to finish by finding the page in the tree view. - // This is a bit of a roundabout way to find items in a tree view since we dont use umbracoTreeItem - // but we must be able to wait for the save event to finish, and we can't do that with umbracoTreeItem - cy.get('[data-element="tree-item-templates"] > :nth-child(2) > .umb-animated > .umb-tree-item__inner > .umb-tree-item__label') - .contains(name).should('be.visible', { timeout: 10000 }); - // Now that the auto save event has finished we can save - // and there wont be any duplicates or file in use errors. - cy.get('.btn-success').click(); - - //Assert - cy.umbracoSuccessNotification().should('be.visible'); - // For some reason cy.umbracoErrorNotification tries to click the element which is not possible - // if it doesn't actually exist, making should('not.be.visible') impossible. - cy.get('.umb-notifications__notifications > .alert-error').should('not.exist'); - - //Clean up - cy.umbracoEnsureTemplateNameNotExists(name); - }); - - it('Unsaved changes stay', () => { - const name = "Templates Unsaved Changes Stay test"; - const edit = "var num = 5;"; - cy.umbracoEnsureTemplateNameNotExists(name); - - const template = new TemplateBuilder() - .withName(name) - .withContent('@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage\n') - .build(); - - cy.saveTemplate(template); - - navigateToSettings(); - - // Open partial view - cy.umbracoTreeItem("settings", ["Templates", name]).click(); - // Edit - cy.get('.ace_content').type(edit); - cy.get('.ace_content').contains(edit).should('be.visible'); - cy.get('.btn-success').should('be.visible') - - // Navigate away - cy.umbracoSection('content'); - // Click stay button - cy.get('umb-button[label="Stay"] button:enabled').click(); - - // Assert - // That the same document is open - cy.get('#headerName').should('have.value', name); - cy.get('.ace_content').contains(edit); - - cy.umbracoEnsureTemplateNameNotExists(name); - }); - - it('Discard unsaved changes', () => { - const name = "Discard changes test"; - const edit = "var num = 5;"; - - cy.umbracoEnsureTemplateNameNotExists(name); - - const template = new TemplateBuilder() - .withName(name) - .withContent('@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage\n') - .build(); - - cy.saveTemplate(template); - - navigateToSettings(); - - // Open partial view - cy.umbracoTreeItem("settings", ["Templates", name]).click(); - // Edit - cy.get('.ace_content').type(edit); - cy.get('.ace_content').contains(edit).should('be.visible'); - cy.get('.btn-success').should('be.visible') - - // Navigate away - cy.umbracoSection('content'); - // Click discard - cy.get('umb-button[label="Discard changes"] button:enabled').click(); - // Navigate back - cy.umbracoSection('settings'); - - // Asserts - cy.get('.ace_content').should('not.contain', edit); - // cy.umbracoPartialViewExists(fileName).then(exists => { expect(exists).to.be.false; }); TODO: Switch to template - cy.umbracoEnsureTemplateNameNotExists(name); - }); - - it('Insert macro', () => { - const name = 'InsertMacroTest'; - - cy.umbracoEnsureTemplateNameNotExists(name); - cy.umbracoEnsureMacroNameNotExists(name); - - const template = new TemplateBuilder() - .withName(name) - .withContent('') - .build(); - - cy.saveTemplate(template); - - cy.saveMacro(name); - - navigateToSettings(); - cy.umbracoTreeItem("settings", ["Templates", name]).click(); - // Insert macro - cy.umbracoButtonByLabelKey('general_insert').click(); - cy.get('.umb-insert-code-box__title').contains('Macro').click(); - cy.get(`.umb-card-grid-item[title='${name}']`).click('bottom'); - - // Assert - cy.get('.ace_content').contains('@await Umbraco.RenderMacroAsync("' + name + '")').should('exist'); - - // Clean - cy.umbracoEnsureTemplateNameNotExists(name); - cy.umbracoEnsureMacroNameNotExists(name); - }); - - it('Insert value', () => { - const name = 'Insert Value Test'; - - cy.umbracoEnsureTemplateNameNotExists(name); - - const partialView = new TemplateBuilder() - .withName(name) - .withContent('') - .build(); - - cy.saveTemplate(partialView); - - navigateToSettings(); - cy.umbracoTreeItem("settings", ["Templates", name]).click(); - - // Insert value - cy.umbracoButtonByLabelKey('general_insert').click(); - cy.get('.umb-insert-code-box__title').contains('Value').click(); - cy.get('select').select('umbracoBytes'); - cy.umbracoButtonByLabelKey('general_submit').click(); - - // assert - cy.get('.ace_content').contains('@Model.Value("umbracoBytes")').should('exist'); - - // Clean - cy.umbracoEnsureTemplateNameNotExists(name); - }); - -}); diff --git a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Tabs/tabs.ts b/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Tabs/tabs.ts deleted file mode 100644 index 8a6235d1e4..0000000000 --- a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Tabs/tabs.ts +++ /dev/null @@ -1,551 +0,0 @@ -/// -import { - DocumentTypeBuilder, - AliasHelper - } from 'umbraco-cypress-testhelpers'; - - const tabsDocTypeName = 'Tabs Test Document'; - const tabsDocTypeAlias = AliasHelper.toAlias(tabsDocTypeName); - - context('Tabs', () => { - - beforeEach(() => { - cy.umbracoLogin(Cypress.env('username'), Cypress.env('password'), false); - }); - - afterEach(() => { - cy.umbracoEnsureDocumentTypeNameNotExists(tabsDocTypeName); - cy.umbracoEnsureTemplateNameNotExists(tabsDocTypeName); - }); - - function OpenDocTypeFolder(){ - cy.umbracoSection('settings'); - // We have to wait in case the execution is slow, otherwise we'll try and click the item before it appears in the UI - cy.get('li .umb-tree-root:contains("Settings")').should("be.visible"); - cy.umbracoTreeItem('settings', ["Document Types", tabsDocTypeName]).click(); - } - - function CreateDocWithTabAndNavigate(){ - cy.umbracoEnsureDocumentTypeNameNotExists(tabsDocTypeName); - const tabsDocType = new DocumentTypeBuilder() - .withName(tabsDocTypeName) - .withAlias(tabsDocTypeAlias) - .withAllowAsRoot(true) - .withDefaultTemplate(tabsDocTypeAlias) - .addTab() - .withName('Tab 1') - .addGroup() - .withName('Tab group') - .addUrlPickerProperty() - .withAlias("urlPicker") - .done() - .done() - .done() - .build(); - cy.saveDocumentType(tabsDocType); - OpenDocTypeFolder(); - } - - it('Create tab', () => { - cy.umbracoEnsureDocumentTypeNameNotExists(tabsDocTypeName); - cy.deleteAllContent(); - const tabsDocType = new DocumentTypeBuilder() - .withName(tabsDocTypeName) - .withAlias(tabsDocTypeAlias) - .withAllowAsRoot(true) - .withDefaultTemplate(tabsDocTypeAlias) - .addGroup() - .withName('Tabs1Group') - .addUrlPickerProperty() - .withAlias('picker') - .done() - .done() - .build(); - cy.saveDocumentType(tabsDocType); - OpenDocTypeFolder(); - //Create a tab - cy.get('.umb-group-builder__tabs__add-tab').click(); - cy.get('ng-form.ng-invalid > .umb-group-builder__group-title-input').type('Tab 1'); - //Create a 2nd tab manually - cy.get('.umb-group-builder__tabs__add-tab').click(); - cy.get('ng-form.ng-invalid > .umb-group-builder__group-title-input').type('Tab 2'); - //Create a textstring property - cy.get('[aria-hidden="false"] > .umb-box-content > .umb-group-builder__group-add-property').click(); - cy.get('.editor-label').type('property name'); - cy.get('[data-element="editor-add"]').click(); - - //Search for textstring - cy.get('#datatype-search').type('Textstring'); - - // Choose first item - cy.get('[title="Textstring"]').closest("li").click(); - - // Save property - cy.get('.btn-success').last().click(); - cy.umbracoButtonByLabelKey('buttons_save').click(); - //Assert - cy.umbracoSuccessNotification().should('be.visible'); - cy.get('[title="tab1"]').should('be.visible'); - cy.get('[title="tab2"]').should('be.visible'); - }); - - it('Delete tabs', () => { - CreateDocWithTabAndNavigate(); - //Check if tab is there, else if it wasnt created, this test would always pass - cy.get('[title="aTab 1"]').should('be.visible'); - //Delete a tab - cy.get('.btn-reset > [icon="icon-trash"]').first().click(); - cy.get('.umb-button > .btn').last().click(); - cy.umbracoButtonByLabelKey('buttons_save').click(); - //Assert - cy.get('[title="aTab 1"]').should('not.exist'); - }); - - it('Delete property in tab', () => { - cy.umbracoEnsureDocumentTypeNameNotExists(tabsDocTypeName); - const tabsDocType = new DocumentTypeBuilder() - .withName(tabsDocTypeName) - .withAlias(tabsDocTypeAlias) - .withAllowAsRoot(true) - .withDefaultTemplate(tabsDocTypeAlias) - .addTab() - .withName('Tab 1') - .addGroup() - .withName('Tab group') - .addUrlPickerProperty() - .withAlias("urlPicker") - .done() - .addContentPickerProperty() - .withAlias('picker') - .done() - .done() - .done() - .build(); - cy.saveDocumentType(tabsDocType); - OpenDocTypeFolder(); - cy.get('[aria-label="Delete property"]').last().click(); - cy.umbracoButtonByLabelKey('actions_delete').click(); - cy.umbracoButtonByLabelKey('buttons_save').click() - //Assert - cy.umbracoSuccessNotification().should('be.visible'); - cy.get('[title=urlPicker]').should('be.visible'); - cy.get('[title=picker]').should('not.exist'); - }); - - it('Delete group in tab', () => { - cy.umbracoEnsureDocumentTypeNameNotExists(tabsDocTypeName); - const tabsDocType = new DocumentTypeBuilder() - .withName(tabsDocTypeName) - .withAlias(tabsDocTypeAlias) - .withAllowAsRoot(true) - .withDefaultTemplate(tabsDocTypeAlias) - .addTab() - .withName('Tab 1') - .addGroup() - .withName('Tab group') - .addUrlPickerProperty() - .withAlias("urlPicker") - .done() - .done() - .addGroup() - .withName('Content Picker Group') - .addContentPickerProperty() - .withAlias('picker') - .done() - .done() - .done() - .build(); - cy.saveDocumentType(tabsDocType); - OpenDocTypeFolder(); - //Delete group - cy.get('.umb-group-builder__group-remove > [icon="icon-trash"]').eq(1).click(); - cy.umbracoButtonByLabelKey('actions_delete').click(); - cy.umbracoButtonByLabelKey('buttons_save').click() - //Assert - cy.umbracoSuccessNotification().should('be.visible'); - cy.get('[title=picker]').should('be.visible'); - cy.get('[title=urlPicker]').should('not.exist'); - }); - - it('Reorders tab', () => { - cy.umbracoEnsureDocumentTypeNameNotExists(tabsDocTypeName); - const tabsDocType = new DocumentTypeBuilder() - .withName(tabsDocTypeName) - .withAlias(tabsDocTypeAlias) - .withAllowAsRoot(true) - .withDefaultTemplate(tabsDocTypeAlias) - .addTab() - .withName('Tab 1') - .addGroup() - .withName('Tab group 1') - .addUrlPickerProperty() - .withLabel('Url picker 1') - .withAlias("urlPicker") - .done() - .done() - .done() - .addTab() - .withName('Tab 2') - .addGroup() - .withName('Tab group 2') - .addUrlPickerProperty() - .withLabel('Url picker 2') - .withAlias("pickerTab 2") - .done() - .done() - .done() - .addTab() - .withName('Tab 3') - .addGroup() - .withName('Tab group') - .addUrlPickerProperty() - .withLabel('Url picker 3') - .withAlias('pickerTab3') - .done() - .done() - .done() - .build(); - cy.saveDocumentType(tabsDocType); - OpenDocTypeFolder(); - //Check if there are any tabs - cy.get('ng-form.ng-valid-required > .umb-group-builder__group-title-input').should('be.visible'); - cy.get('[alias="reorder"]').click(); - //Type order in - cy.get('.umb-group-builder__tab-sort-order > .umb-property-editor-tiny').first().type('3'); - cy.get('[alias="reorder"]').click(); - //Assert - cy.get('.umb-group-builder__tab-name').eq(0).invoke('attr', 'title').should('eq', 'aTab 2') - cy.get('.umb-group-builder__tab-name').eq(1).invoke('attr', 'title').should('eq', 'aTab 3') - cy.get('.umb-group-builder__group-title-input').eq(0).invoke('attr', 'title').should('eq', 'aTab 1') - }); - - it('Reorders groups in a tab', () => { - cy.umbracoEnsureDocumentTypeNameNotExists(tabsDocTypeName); - const tabsDocType = new DocumentTypeBuilder() - .withName(tabsDocTypeName) - .withAlias(tabsDocTypeAlias) - .withAllowAsRoot(true) - .withDefaultTemplate(tabsDocTypeAlias) - .addTab() - .withName('Tab 1') - .addGroup() - .withName('Tab group 1') - .addUrlPickerProperty() - .withLabel('Url picker 1') - .withAlias("urlPicker") - .done() - .done() - .addGroup() - .withName('Tab group 2') - .addUrlPickerProperty() - .withLabel('Url picker 2') - .withAlias('urlPickerTwo') - .done() - .done() - .done() - .build(); - cy.saveDocumentType(tabsDocType); - OpenDocTypeFolder(); - cy.get('[alias="reorder"]').click(); - cy.get('.umb-property-editor-tiny').eq(2).type('1'); - - cy.get('[alias="reorder"]').click(); - cy.umbracoButtonByLabelKey('buttons_save').click(); - //Assert - cy.umbracoSuccessNotification().should('be.visible'); - cy.get('.umb-group-builder__group-title-input').eq(2) - .invoke('attr', 'title').should('eq', 'aTab 1/aTab group 2'); - }); - - it('Reorders properties in a tab', () => { - cy.umbracoEnsureDocumentTypeNameNotExists(tabsDocTypeName); - const tabsDocType = new DocumentTypeBuilder() - .withName(tabsDocTypeName) - .withAlias(tabsDocTypeAlias) - .withAllowAsRoot(true) - .withDefaultTemplate(tabsDocTypeAlias) - .addTab() - .withName('Tab 1') - .addGroup() - .withName('Tab group') - .addUrlPickerProperty() - .withLabel('PickerOne') - .withAlias("urlPicker") - .done() - .addUrlPickerProperty() - .withLabel('PickerTwo') - .withAlias('urlPickerTwo') - .done() - .done() - .done() - .build(); - cy.saveDocumentType(tabsDocType); - OpenDocTypeFolder(); - //Reorder - cy.get('[alias="reorder"]').click(); - cy.get('.umb-group-builder__group-sort-value').first().type('2'); - cy.get('[alias="reorder"]').click(); - cy.umbracoButtonByLabelKey('buttons_save').click(); - //Assert - cy.umbracoSuccessNotification().should('be.visible'); - cy.get('.umb-locked-field__input').last().invoke('attr', 'title').should('eq', 'urlPicker'); - }); - - it('Tab name cannot be empty', () => { - CreateDocWithTabAndNavigate(); - cy.get('.umb-group-builder__group-title-input').first().clear(); - cy.umbracoButtonByLabelKey('buttons_save').click(); - //Assert - cy.umbracoErrorNotification().should('be.visible'); - }); - - it('Two tabs cannot have the same name', () => { - cy.umbracoEnsureDocumentTypeNameNotExists(tabsDocTypeName); - const tabsDocType = new DocumentTypeBuilder() - .withName(tabsDocTypeName) - .withAlias(tabsDocTypeAlias) - .withAllowAsRoot(true) - .withDefaultTemplate(tabsDocTypeAlias) - .addTab() - .withName('Tab 1') - .addGroup() - .withName('Tab group') - .addUrlPickerProperty() - .withAlias("urlPicker") - .done() - .done() - .done() - .build(); - cy.saveDocumentType(tabsDocType); - OpenDocTypeFolder(); - //Create a 2nd tab manually - cy.get('.umb-group-builder__tabs__add-tab').click(); - cy.get('ng-form.ng-invalid > .umb-group-builder__group-title-input').type('Tab 1'); - cy.umbracoButtonByLabelKey('buttons_save').click(); - //Assert - cy.umbracoErrorNotification().should('be.visible'); - }); - - it('Group name cannot be empty', () => { - CreateDocWithTabAndNavigate(); - cy.get('.clearfix > .-placeholder').click(); - cy.umbracoButtonByLabelKey('buttons_save').click(); - //Assert - cy.umbracoErrorNotification().should('be.visible'); - }); - - it('Group name cannot have the same name', () => { - CreateDocWithTabAndNavigate(); - cy.get('.clearfix > .-placeholder').click(); - cy.get('.umb-group-builder__group-title-input').last().type('Tab group'); - cy.umbracoButtonByLabelKey('buttons_save').click(); - //Assert - cy.umbracoErrorNotification().should('be.visible'); - }); - - it('Drag a group into another tab', () => { - cy.umbracoEnsureDocumentTypeNameNotExists(tabsDocTypeName); - const tabsDocType = new DocumentTypeBuilder() - .withName(tabsDocTypeName) - .withAlias(tabsDocTypeAlias) - .withAllowAsRoot(true) - .withDefaultTemplate(tabsDocTypeAlias) - .addTab() - .withName('Tab 1') - .addGroup() - .withName('Tab group') - .addUrlPickerProperty() - .withAlias("urlPicker") - .done() - .done() - .done() - .addTab() - .withName('Tab 2') - .addGroup() - .withName('Tab group tab 2') - .addUrlPickerProperty() - .withAlias('urlPickerTabTwo') - .done() - .done() - .addGroup() - .withName('Tab group 2') - .addUrlPickerProperty() - .withAlias('urlPickerTwo') - .done() - .done() - .done() - .build(); - cy.saveDocumentType(tabsDocType); - OpenDocTypeFolder(); - cy.get('[alias="reorder"]').click(); - cy.get('body') - .then(($body) => { - while($body.find('.umb-group-builder__tabs-overflow--right > .caret').hasClass('active')){ - cy.click(); - } - }); - cy.get('.umb-group-builder__tab').last().click(); - cy.get('.umb-group-builder__group-title-icon').last().trigger('mousedown', { which: 1 }) - cy.get('.umb-group-builder__tab').eq(1).trigger('mousemove', {which: 1, force: true}); - cy.get('.umb-group-builder__tab').eq(1).should('have.class', 'is-active').trigger('mouseup', {force:true}); - cy.umbracoButtonByLabelKey('buttons_save').click(); - //Assert - cy.umbracoSuccessNotification().should('be.visible'); - cy.get('[title="aTab 1/aTab group 2"]').should('be.visible'); - }); - - it('Drag and drop reorders a tab', () => { - cy.umbracoEnsureDocumentTypeNameNotExists(tabsDocTypeName); - const tabsDocType = new DocumentTypeBuilder() - .withName(tabsDocTypeName) - .withAlias(tabsDocTypeAlias) - .withAllowAsRoot(true) - .withDefaultTemplate(tabsDocTypeAlias) - .addTab() - .withName('Tab 1') - .addGroup() - .withName('Tab group') - .addUrlPickerProperty() - .withAlias("urlPicker") - .done() - .done() - .done() - .addTab() - .withName('Tab 2') - .addGroup() - .withName('Tab group tab 2') - .addUrlPickerProperty() - .withAlias('urlPickerTabTwo') - .done() - .done() - .addGroup() - .withName('Tab group 2') - .addUrlPickerProperty() - .withAlias('urlPickerTwo') - .done() - .done() - .done() - .build(); - cy.saveDocumentType(tabsDocType); - OpenDocTypeFolder(); - cy.get('[alias="reorder"]').click(); - //Scroll right so we can see tab 2 - cy.get('body') - .then(($body) => { - while($body.find('.umb-group-builder__tabs-overflow--right > .caret').hasClass('active')){ - cy.click(); - } - }); - cy.get('.umb-group-builder__tab-title-icon').eq(1).trigger('mousedown', { which: 1 }) - cy.get('.umb-group-builder__tab').eq(1).trigger('mousemove', {which: 1, force: true}); - cy.get('.umb-group-builder__tab').eq(1).should('have.class', 'is-active').trigger('mouseup', {force:true}); - cy.get('[alias="reorder"]').click(); - cy.umbracoButtonByLabelKey('buttons_save').click(); - //Assert - cy.umbracoSuccessNotification().should('be.visible'); - cy.get('[title="aTab 2"]').should('be.visible'); - }); - - it('Drags and drops a property in a tab', () => { - cy.umbracoEnsureDocumentTypeNameNotExists(tabsDocTypeName); - const tabsDocType = new DocumentTypeBuilder() - .withName(tabsDocTypeName) - .withAlias(tabsDocTypeAlias) - .withAllowAsRoot(true) - .withDefaultTemplate(tabsDocTypeAlias) - .addTab() - .withName('Tab 1') - .addGroup() - .withName('Tab group') - .addUrlPickerProperty() - .withAlias("urlPicker") - .withLabel('UrlPickerOne') - .done() - .done() - .done() - .addTab() - .withName('Tab 2') - .addGroup() - .withName('Tab group tab 2') - .addUrlPickerProperty() - .withAlias('urlPickerTabTwo') - .withLabel('UrlPickerTabTwo') - .done() - .addUrlPickerProperty() - .withAlias('urlPickerTwo') - .withLabel('UrlPickerTwo') - .done() - .done() - .done() - .build(); - cy.saveDocumentType(tabsDocType); - OpenDocTypeFolder(); - cy.get('[alias="reorder"]').click(); - //Scroll so we are sure we see tab 2 - cy.get('body') - .then(($body) => { - while($body.find('.umb-group-builder__tabs-overflow--right > .caret').hasClass('active')){ - cy.click(); - } - }); - //Navigate to tab 2 - cy.get('.umb-group-builder__tab').last().click(); - cy.get('.umb-group-builder__property-meta > .flex > .icon').eq(1).trigger('mousedown', {which: 1}) - cy.get('.umb-group-builder__tab').eq(1).trigger('mousemove', {which: 1, force: true}); - cy.get('.umb-group-builder__tab').eq(1).should('have.class', 'is-active'); - cy.get('.umb-group-builder__property') - .first().trigger('mousemove', {which: 1, force: true}).trigger('mouseup', {which: 1, force:true}); - cy.get('[alias="reorder"]').click(); - cy.umbracoButtonByLabelKey('buttons_save').click(); - //Assert - cy.umbracoSuccessNotification().should('be.visible'); - cy.get('[title="urlPickerTabTwo"]').should('be.visible'); - }); - - it('Drags and drops a group and converts to tab', () => { - cy.umbracoEnsureDocumentTypeNameNotExists(tabsDocTypeName); - const tabsDocType = new DocumentTypeBuilder() - .withName(tabsDocTypeName) - .withAlias(tabsDocTypeAlias) - .withAllowAsRoot(true) - .withDefaultTemplate(tabsDocTypeAlias) - .addTab() - .withName('Tab 1') - .addGroup() - .withName('Tab group') - .addUrlPickerProperty() - .withAlias("urlPicker") - .withLabel('UrlPickerOne') - .done() - .done() - .addGroup() - .withName('Tab group 2') - .addUrlPickerProperty() - .withAlias('urlPickerTwo') - .withLabel('UrlPickerTwo') - .done() - .done() - .done() - .addTab() - .withName('Tab 2') - .addGroup() - .withName('Tab group tab 2') - .addUrlPickerProperty() - .withAlias('urlPickerTabTwo') - .withLabel('UrlPickerTabTwo') - .done() - .done() - .done() - .build(); - cy.saveDocumentType(tabsDocType); - OpenDocTypeFolder(); - cy.get('[alias="reorder"]').click(); - cy.get('.umb-group-builder__group-title-icon').eq(1).trigger('mousedown', {which: 1}) - cy.get('.umb-group-builder__convert-dropzone').trigger('mousemove', {which: 1, force: true}); - cy.get('.umb-group-builder__convert-dropzone').trigger('mouseup', {which: 1, force:true}); - cy.umbracoButtonByLabelKey('buttons_save').click(); - //Assert - cy.umbracoSuccessNotification().should('be.visible'); - cy.get('[title="tabGroup"]').should('be.visible'); - }); - }); diff --git a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Tours/backofficeTour.ts b/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Tours/backofficeTour.ts deleted file mode 100644 index dbb82f53f4..0000000000 --- a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Tours/backofficeTour.ts +++ /dev/null @@ -1,107 +0,0 @@ -/// - -context('Backoffice Tour', () => { - var timeout = 60000; - beforeEach(() => { - //arrange - cy.umbracoLogin(Cypress.env('username'), Cypress.env('password')); - resetTourData(); - }); - - it('Backoffice introduction tour should run', () => { - //act - cy.umbracoGlobalHelp().should("be.visible"); - cy.umbracoGlobalHelp().click(); - runBackOfficeIntroTour(0, 'Start'); - - //assert - cy.get('[data-element="help-tours"]').should("be.visible"); - cy.get('[data-element="help-tours"]').click(); - getPercentage(17, timeout); - }); - - it('Backoffice introduction tour should run then rerun', () => { - //act - cy.umbracoGlobalHelp().should("be.visible"); - cy.umbracoGlobalHelp().click(); - runBackOfficeIntroTour(0, 'Start', timeout); - runBackOfficeIntroTour(17, 'Rerun', timeout); - - //assert - cy.get('[data-element="help-tours"]').should("be.visible"); - cy.get('[data-element="help-tours"]').click(); - cy.umbracoGlobalHelp().should("be.visible"); - getPercentage(17, timeout); - }); - - afterEach(() => { - //cleanup - resetTourData(); - }); -}); - -function getPercentage(percentage, timeout) { - cy.get('[data-element="help-tours"] .umb-progress-circle', { timeout: timeout }).get('[percentage]').contains(percentage + '%'); -} - -function resetTourData() { - var tourStatus = - { - "alias": "umbIntroIntroduction", - "completed": false, - "disabled": true - }; - - cy.getCookie('UMB-XSRF-TOKEN', { log: false }).then((token) => { - cy.request({ - method: 'POST', - url: '/umbraco/backoffice/UmbracoApi/CurrentUser/PostSetUserTour', - followRedirect: false, - headers: { - ContentType: 'application/json', - 'X-UMB-XSRF-TOKEN': token.value, - }, - body: tourStatus, - }).then((resp) => { - return; - }); - }) -} - -function runBackOfficeIntroTour(percentageComplete, buttonText, timeout) { - cy.get('[data-element="help-tours"]').should("be.visible"); - cy.get('[data-element="help-tours"]').click(); - cy.get('[data-element="help-tours"] .umb-progress-circle', { timeout: timeout }).get('[percentage]').contains(percentageComplete + '%', { timeout: timeout }); - cy.get('[data-element="help-tours"]').click(); - cy.get('[data-element="tour-umbIntroIntroduction"] .umb-button').should("be.visible"); - cy.get('[data-element="tour-umbIntroIntroduction"] .umb-button').contains(buttonText); - cy.get('[data-element="tour-umbIntroIntroduction"] .umb-button').click(); - //act - cy.get('.umb-tour-step', { timeout: timeout }).should('be.visible'); - cy.get('.umb-tour-step__footer').should('be.visible'); - cy.get('.umb-tour-step__counter').should('be.visible'); - - for (let i = 1; i < 8; i++) { - - if(i == 4) { - continue - } - cy.get('.umb-tour-step__counter').contains(i + '/13'); - cy.get('.umb-tour-step__footer .umb-button').should('be.visible').click(); - } - cy.umbracoGlobalUser().click() - cy.get('.umb-tour-step__counter', { timeout: timeout }).contains('9/13'); - cy.get('.umb-tour-step__footer .umb-button').should('be.visible').click(); - cy.get('.umb-tour-step__counter', { timeout: timeout }).contains('10/13'); - cy.get('[data-element~="overlay-user"] [data-element="button-overlayClose"]').should('be.visible').click(); - cy.get('.umb-tour-step__counter', { timeout: timeout }).contains('11/13'); - cy.umbracoGlobalHelp().click() - - for (let i = 12; i <= 13; i++) { - cy.get('.umb-tour-step__counter', { timeout: timeout }).contains(i + '/13'); - cy.get('.umb-tour-step__footer .umb-button').should('be.visible').click(); - } - cy.get('.umb-tour-step__footer .umb-button').should('be.visible').click(); - - cy.umbracoGlobalHelp().should("be.visible"); -} \ No newline at end of file diff --git a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Users/userGroups.ts b/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Users/userGroups.ts deleted file mode 100644 index cd4b022544..0000000000 --- a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Users/userGroups.ts +++ /dev/null @@ -1,85 +0,0 @@ -/// -import { UserGroupBuilder } from 'umbraco-cypress-testhelpers'; - -context('User Groups', () => { - - function navigateToUserGroups() { - cy.umbracoSection('users'); - cy.get('[data-element="sub-view-userGroups"]').click(); - } - - beforeEach(() => { - cy.umbracoLogin(Cypress.env('username'), Cypress.env('password')); - }); - - it('Create user group', () => { - const name = "Test Group"; - - cy.umbracoEnsureUserGroupNameNotExists(name); - - navigateToUserGroups(); - cy.umbracoButtonByLabelKey("actions_createGroup").click(); - - //Type name - cy.umbracoEditorHeaderName(name); - - // Assign sections - cy.get('.umb-box:nth-child(1) .umb-property:nth-child(1) localize').click(); - cy.get('.umb-tree-item__inner').click({multiple:true, timeout: 10000}); - cy.get('.btn-success').last().click(); - - // Save - cy.get('.btn-success').click(); - - //Assert - cy.umbracoSuccessNotification().should('be.visible'); - - //Clean up - cy.umbracoEnsureUserGroupNameNotExists(name); - }); - - it('Can delete user group', () => { - // Create user group - const groupName = "Delete user group test" - cy.umbracoEnsureUserGroupNameNotExists(groupName); - - const userGroup = new UserGroupBuilder() - .withName(groupName) - .build(); - - cy.saveUserGroup(userGroup); - navigateToUserGroups(); - - // Delete the user group - cy.get('.umb-table-body > :nth-child(2)').click(); - cy.umbracoButtonByLabelKey("general_delete").click(); - cy.get('umb-button[alias="overlaySubmit"]').click(); - - cy.umbracoSuccessNotification().should('be.visible'); - cy.get('.umb-table-body').contains(groupName).should('not.exist'); - - cy.umbracoEnsureUserGroupNameNotExists(groupName); - }); - - it('Cannot delete required groups', () => { - navigateToUserGroups(); - - // There's not really a good way to be 100% sure we'll get the admin group, it should be first, but who knows - // so double check that we actually got the correct one - const administrators = cy.get('.umb-table-body > :nth-child(1)'); - administrators.should('contain', 'Administrators'); - administrators.click({force: true}); - - const sensitive = cy.get('.umb-table-body > :nth-child(3)'); - sensitive.should('contain', 'Sensitive data'); - sensitive.click({force: true}); - - const translators = cy.get('.umb-table-body > :nth-child(4)'); - translators.should('contain', 'Translators'); - translators.click({force: true}); - - // Now that we've clicked all that we shouldn't be able to delete, ensure that the delete button does not show up - cy.get('.umb-editor-sub-header').should('not.contain', 'Delete'); - }); -}); - diff --git a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Users/users.ts b/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Users/users.ts deleted file mode 100644 index 8cf2eb8579..0000000000 --- a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Users/users.ts +++ /dev/null @@ -1,92 +0,0 @@ -/// -context('Users', () => { - - const name = "Alice Bobson"; - const email = "alice-bobson@acceptancetest.umbraco"; - const startContentIds = []; - const startMediaIds = []; - const userGroups = ["admin"]; - - const userData = - { - "id": -1, - "parentId": -1, - "name": name, - "username": email, - "culture": "en-US", - "email": email, - "startContentIds": startContentIds, - "startMediaIds": startMediaIds, - "userGroups": userGroups, - "message": "" - }; - - beforeEach(() => { - cy.umbracoLogin(Cypress.env('username'), Cypress.env('password')); - }); - - afterEach(() => { - //Clean up - cy.umbracoEnsureUserEmailNotExists(email); - }); - - function createUser(){ - let url = '/umbraco/backoffice/umbracoapi/users/PostCreateUser'; - cy.umbracoApiRequest(url, 'POST', userData); - } - it('Create user', () => { - - cy.umbracoEnsureUserEmailNotExists(email); - cy.umbracoSection('users'); - cy.umbracoButtonByLabelKey("user_createUser").click(); - - - cy.get('input[name="name"]').type(name); - cy.get('input[name="email"]').type(email); - - cy.get('.umb-node-preview-add').click(); - cy.get('.umb-user-group-picker-list-item:nth-child(1) > .umb-user-group-picker__action').click(); - cy.get('.umb-user-group-picker-list-item:nth-child(2) > .umb-user-group-picker__action').click(); - cy.get('.btn-success').click(); - - cy.get('.umb-button > .btn > .umb-button__content').click(); - - cy.umbracoButtonByLabelKey("user_goToProfile").should('be.visible'); - }); - - it('Update user', () => { - - // Ensure user doesn't exist - cy.umbracoEnsureUserEmailNotExists(email); - - //Create user through API - createUser(); - - // Go to the user and edit their name - cy.umbracoSection('users'); - cy.get('.umb-user-card__name').contains(name).click(); - cy.get('#headerName').type('{movetoend}son'); - cy.umbracoButtonByLabelKey('buttons_save').click(); - - // assert save succeeds - cy.umbracoSuccessNotification().should('be.visible'); - }) - - it('Delete user', () => { - - // Ensure user doesn't exist - cy.umbracoEnsureUserEmailNotExists(email); - - //Create user through API - createUser(); - - // Go to the user and delete them - cy.umbracoSection('users'); - cy.get('.umb-user-card__name').contains(name).click(); - cy.umbracoButtonByLabelKey("user_deleteUser").click(); - cy.get('umb-button[label="Yes, delete"]').click(); - - // assert deletion succeeds - cy.umbracoSuccessNotification().should('be.visible'); - }) -}); diff --git a/tests/Umbraco.Tests.AcceptanceTest/cypress/plugins/index.js b/tests/Umbraco.Tests.AcceptanceTest/cypress/plugins/index.js deleted file mode 100644 index 30988b82cf..0000000000 --- a/tests/Umbraco.Tests.AcceptanceTest/cypress/plugins/index.js +++ /dev/null @@ -1,45 +0,0 @@ -/// -// *********************************************************** -// This example plugins/index.js can be used to load plugins -// -// You can change the location of this file or turn off loading -// the plugins file with the 'pluginsFile' configuration option. -// -// You can read more here: -// https://on.cypress.io/plugins-guide -// *********************************************************** - -// This function is called when a project is opened or re-opened (e.g. due to -// the project's config changing) -const del = require('del') -const { isFileExist } = require('cy-verify-downloads'); - -/** - * @type {Cypress.PluginConfig} - */ -module.exports = (on, config) => { - // `on` is used to hook into various events Cypress emits - // `config` is the resolved Cypress config - const baseUrl = config.env.baseUrl || null; - - if (baseUrl) { - config.baseUrl = baseUrl; - } - - on('task', { isFileExist }) - on('after:spec', (spec, results) => { - if(results.stats.failures === 0 && results.video) { - // `del()` returns a promise, so it's important to return it to ensure - // deleting the video is finished before moving - return del(results.video) - } - }) - - -on('task', { - isFileExist -}); - - - return config; -} \ No newline at end of file diff --git a/tests/Umbraco.Tests.AcceptanceTest/cypress/support/commands.js b/tests/Umbraco.Tests.AcceptanceTest/cypress/support/commands.js deleted file mode 100644 index 5056c05036..0000000000 --- a/tests/Umbraco.Tests.AcceptanceTest/cypress/support/commands.js +++ /dev/null @@ -1,98 +0,0 @@ -// *********************************************** -// This example commands.js shows you how to -// create various custom commands and overwrite -// existing commands. -// -// For more comprehensive examples of custom -// commands please read more here: -// https://on.cypress.io/custom-commands -// *********************************************** -// -// -// -- This is a parent command -- -// Cypress.Commands.add("login", (email, password) => { ... }) -// -// -// -- This is a child command -- -// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) -// -// -// -- This is a dual command -- -// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) -// -// -// -- This will overwrite an existing command -- -// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) - -import {Command} from 'umbraco-cypress-testhelpers'; -import {Chainable} from './chainable'; -import { JsonHelper } from 'umbraco-cypress-testhelpers'; -require('cy-verify-downloads').addCustomCommand(); -new Chainable(); -new Command().registerCypressCommands(); - -Cypress.Commands.add('umbracoCreateLanguage', (culture, isMandatory = false, fallbackLanguageId = 1) => { - - var langData = - { - "culture": culture, - "isMandatory": isMandatory, - "fallbackLanguageId": fallbackLanguageId - }; - - // Create language through API - cy.getCookie('UMB-XSRF-TOKEN', { log: false }).then((token) => { - cy.request({ - method: 'POST', - url: '/umbraco/backoffice/umbracoapi/language/SaveLanguage', - followRedirect: true, - headers: { - Accept: 'application/json', - 'X-UMB-XSRF-TOKEN': token.value, - }, - body: langData, - log: false, - }).then((response) => { - return; - }); - }); -}); - -Cypress.Commands.add('umbracoEnsureLanguageCultureNotExists', (culture) => { - cy.getCookie('UMB-XSRF-TOKEN', { log: false }).then((token) => { - cy.request({ - method: 'GET', - url: '/umbraco/backoffice/umbracoapi/language/GetAllLanguages', - followRedirect: true, - headers: { - Accept: 'application/json', - 'X-UMB-XSRF-TOKEN': token.value, - }, - log: false, - }).then((response) => { - const searchBody = JsonHelper.getBody(response); - if (searchBody.length > 0) { - let languageId = null; - for (const sb of searchBody) { - if (sb.culture === culture) { - languageId = sb.id; - } - } - - if (languageId !== null) { - cy.request({ - method: 'POST', - url: '/umbraco/backoffice/umbracoapi/language/DeleteLanguage?id=' + languageId, - followRedirect: false, - headers: { - ContentType: 'application/json', - 'X-UMB-XSRF-TOKEN': token.value, - }, - }).then((resp) => { - return; - }); - } - } - }); - }); -}); \ No newline at end of file diff --git a/tests/Umbraco.Tests.AcceptanceTest/cypress/support/index.ts b/tests/Umbraco.Tests.AcceptanceTest/cypress/support/index.ts deleted file mode 100644 index d68db96df2..0000000000 --- a/tests/Umbraco.Tests.AcceptanceTest/cypress/support/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -// *********************************************************** -// This example support/index.js is processed and -// loaded automatically before your test files. -// -// This is a great place to put global configuration and -// behavior that modifies Cypress. -// -// You can change the location of this file or turn off -// automatically serving support files with the -// 'supportFile' configuration option. -// -// You can read more here: -// https://on.cypress.io/configuration -// *********************************************************** - -// Import commands.js using ES2015 syntax: -import './commands' - -// Alternatively you can use CommonJS syntax: -// require('./commands') diff --git a/tests/Umbraco.Tests.AcceptanceTest/cypress/tsconfig.json b/tests/Umbraco.Tests.AcceptanceTest/cypress/tsconfig.json deleted file mode 100644 index 70cf2f6751..0000000000 --- a/tests/Umbraco.Tests.AcceptanceTest/cypress/tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "../tsconfig.json", - "include": [ - "../node_modules/cypress", - "*/*.ts" - ] -} diff --git a/tests/Umbraco.Tests.AcceptanceTest/cypress/typings.d.ts b/tests/Umbraco.Tests.AcceptanceTest/cypress/typings.d.ts deleted file mode 100644 index 88626d5f0f..0000000000 --- a/tests/Umbraco.Tests.AcceptanceTest/cypress/typings.d.ts +++ /dev/null @@ -1,30 +0,0 @@ -// type definitions for Cypress object "cy" -/// - -// type definitions for custom commands like "createDefaultTodos" -// - -export {}; - -declare global { - // eslint-disable-next-line @typescript-eslint/no-namespace - namespace Cypress { - interface Chainable { - /** - * Checks to see if the language with specified culture does not exist - * If it does it will automatically delete it - * @param {string} culture Culture of language to delete - * @example cy.umbracoEnsureLanguageCultureNotExists('da-DK'); - */ - umbracoEnsureLanguageCultureNotExists: (culture: string) => Chainable; - /** - * Creates a language from a culture - * @param {string} culture Culture of the language - fx "da_DK" - * @param {boolean} isMandatory Set whether the language is mandatory or not. Defaults to false - * @param {string} fallbackLanguageId of the language to fallback to. Defaults to 1 which is en_US - * @example cy.umbracoCreateLanguage('da', true, '1'); - */ - umbracoCreateLanguage: (culture: string, isMandatory: boolean, fallbackLanguageId: string) => Chainable; - } - } -} \ No newline at end of file diff --git a/tests/Umbraco.Tests.AcceptanceTest/fixtures/mediaLibrary/Article.pdf b/tests/Umbraco.Tests.AcceptanceTest/fixtures/mediaLibrary/Article.pdf new file mode 100644 index 0000000000..cf147cc876 Binary files /dev/null and b/tests/Umbraco.Tests.AcceptanceTest/fixtures/mediaLibrary/Article.pdf differ diff --git a/tests/Umbraco.Tests.AcceptanceTest/fixtures/mediaLibrary/Audio.mp3 b/tests/Umbraco.Tests.AcceptanceTest/fixtures/mediaLibrary/Audio.mp3 new file mode 100644 index 0000000000..5ad47fc87e Binary files /dev/null and b/tests/Umbraco.Tests.AcceptanceTest/fixtures/mediaLibrary/Audio.mp3 differ diff --git a/tests/Umbraco.Tests.AcceptanceTest/fixtures/mediaLibrary/File.txt b/tests/Umbraco.Tests.AcceptanceTest/fixtures/mediaLibrary/File.txt new file mode 100644 index 0000000000..1cf34e6f63 --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/fixtures/mediaLibrary/File.txt @@ -0,0 +1 @@ +Me File \ No newline at end of file diff --git a/tests/Umbraco.Tests.AcceptanceTest/fixtures/mediaLibrary/Umbraco.png b/tests/Umbraco.Tests.AcceptanceTest/fixtures/mediaLibrary/Umbraco.png new file mode 100644 index 0000000000..ea29dfc17c Binary files /dev/null and b/tests/Umbraco.Tests.AcceptanceTest/fixtures/mediaLibrary/Umbraco.png differ diff --git a/tests/Umbraco.Tests.AcceptanceTest/fixtures/mediaLibrary/UpdatedFile.txt b/tests/Umbraco.Tests.AcceptanceTest/fixtures/mediaLibrary/UpdatedFile.txt new file mode 100644 index 0000000000..c34ef91fd6 --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/fixtures/mediaLibrary/UpdatedFile.txt @@ -0,0 +1 @@ +UpdatedFile \ No newline at end of file diff --git a/tests/Umbraco.Tests.AcceptanceTest/fixtures/mediaLibrary/VectorGraphics.svg b/tests/Umbraco.Tests.AcceptanceTest/fixtures/mediaLibrary/VectorGraphics.svg new file mode 100644 index 0000000000..605b86e0d7 --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/fixtures/mediaLibrary/VectorGraphics.svg @@ -0,0 +1,50 @@ + + + + + + + + diff --git a/tests/Umbraco.Tests.AcceptanceTest/fixtures/mediaLibrary/Video.mp4 b/tests/Umbraco.Tests.AcceptanceTest/fixtures/mediaLibrary/Video.mp4 new file mode 100644 index 0000000000..fbc43132eb Binary files /dev/null and b/tests/Umbraco.Tests.AcceptanceTest/fixtures/mediaLibrary/Video.mp4 differ diff --git a/tests/Umbraco.Tests.AcceptanceTest/misc/Directory.Build.props b/tests/Umbraco.Tests.AcceptanceTest/misc/Directory.Build.props deleted file mode 100644 index 65a0b30da2..0000000000 --- a/tests/Umbraco.Tests.AcceptanceTest/misc/Directory.Build.props +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/tests/Umbraco.Tests.AcceptanceTest/misc/nuget.config b/tests/Umbraco.Tests.AcceptanceTest/misc/nuget.config index 794b7dace7..c02a885f65 100644 --- a/tests/Umbraco.Tests.AcceptanceTest/misc/nuget.config +++ b/tests/Umbraco.Tests.AcceptanceTest/misc/nuget.config @@ -1,8 +1,7 @@ - - - - - + + + + diff --git a/tests/Umbraco.Tests.AcceptanceTest/misc/umbraco-linux.docker b/tests/Umbraco.Tests.AcceptanceTest/misc/umbraco-linux.docker index 5ae033d6d3..f111f6e48c 100644 --- a/tests/Umbraco.Tests.AcceptanceTest/misc/umbraco-linux.docker +++ b/tests/Umbraco.Tests.AcceptanceTest/misc/umbraco-linux.docker @@ -4,13 +4,15 @@ FROM mcr.microsoft.com/dotnet/sdk:6.0.300 AS build +COPY nuget.config . + WORKDIR /nupkg COPY nupkg . WORKDIR /build RUN dotnet new --install /nupkg/Umbraco.Templates.*.nupkg -RUN dotnet new umbraco --name Cypress -o . --no-restore -RUN dotnet restore -s /nupkg -s https://api.nuget.org/v3/index.json +RUN dotnet new umbraco --name Playwright -o . --no-restore +RUN dotnet restore --configfile /nuget.config RUN dotnet publish --no-restore --configuration Release -o /dist ############################################ @@ -19,16 +21,15 @@ RUN dotnet publish --no-restore --configuration Release -o /dist FROM mcr.microsoft.com/dotnet/aspnet:6.0.5 AS run -WORKDIR /cypress +WORKDIR /app COPY --from=build dist . -ENV ASPNETCORE_URLS="http://0.0.0.0:5000" -ENV Umbraco__CMS__Global__InstallMissingDatabase="true" +ENV ASPNETCORE_URLS="http://0.0.0.0:5000;https://0.0.0.0:5001" ENV ConnectionStrings__umbracoDbDSN_ProviderName="Microsoft.Data.Sqlite" ENV ConnectionStrings__umbracoDbDSN="Data Source=|DataDirectory|/Umbraco.sqlite.db;Cache=Shared;Foreign Keys=True;Pooling=True" ENV Umbraco__CMS__Unattended__InstallUnattended="true" -ENV Umbraco__CMS__Unattended__UnattendedUserName="Cypress Test" -ENV Umbraco__CMS__Unattended__UnattendedUserEmail="cypress@umbraco.com" +ENV Umbraco__CMS__Unattended__UnattendedUserName="Playwright Test" +ENV Umbraco__CMS__Unattended__UnattendedUserEmail="playwright@umbraco.com" ENV Umbraco__CMS__Unattended__UnattendedUserPassword="UmbracoAcceptance123!" -CMD dotnet Cypress.dll +CMD dotnet Playwright.dll diff --git a/tests/Umbraco.Tests.AcceptanceTest/package-lock.json b/tests/Umbraco.Tests.AcceptanceTest/package-lock.json index e0572c1cb3..72f6c5904e 100644 --- a/tests/Umbraco.Tests.AcceptanceTest/package-lock.json +++ b/tests/Umbraco.Tests.AcceptanceTest/package-lock.json @@ -7,65 +7,31 @@ "name": "acceptancetest", "hasInstallScript": true, "dependencies": { - "typescript": "^3.9.2" + "@umbraco/json-models-builders": "^1.0.0", + "@umbraco/playwright-testhelpers": "^1.0.1", + "camelize": "^1.0.0", + "dotenv": "^16.0.2", + "faker": "^4.1.0", + "form-data": "^4.0.0", + "node-fetch": "^2.6.7", + "xhr2": "^0.2.1" }, "devDependencies": { - "cross-env": "^7.0.2", - "cy-verify-downloads": "0.0.5", - "cypress": "8.4.1", + "@playwright/test": "^1.19.2", "del": "^6.0.0", "ncp": "^2.0.0", "prompt": "^1.2.0", - "umbraco-cypress-testhelpers": "^1.0.0-beta-73" + "tslib": "^2.4.0", + "typescript": "^4.8.3" } }, - "node_modules/@cypress/request": { - "version": "2.88.7", - "resolved": "https://registry.npmjs.org/@cypress/request/-/request-2.88.7.tgz", - "integrity": "sha512-FTULIP2rnDJvZDT9t6B4nSfYR40ue19tVmv3wUcY05R9/FPCoMl1nAPJkzWzBCo7ltVn5ThQTbxiMoGBN7k0ig==", + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", "dev": true, - "dependencies": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^8.3.2" - }, "engines": { - "node": ">= 6" - } - }, - "node_modules/@cypress/xvfb": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@cypress/xvfb/-/xvfb-1.2.4.tgz", - "integrity": "sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==", - "dev": true, - "dependencies": { - "debug": "^3.1.0", - "lodash.once": "^4.1.1" - } - }, - "node_modules/@cypress/xvfb/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" + "node": ">=0.1.90" } }, "node_modules/@nodelib/fs.scandir": { @@ -103,32 +69,57 @@ "node": ">= 8" } }, + "node_modules/@playwright/test": { + "version": "1.26.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.26.0.tgz", + "integrity": "sha512-D24pu1k/gQw3Lhbpc38G5bXlBjGDrH5A52MsrH12wz6ohGDeQ+aZg/JFSEsT/B3G8zlJe/EU4EkJK74hpqsjEg==", + "dev": true, + "dependencies": { + "@types/node": "*", + "playwright-core": "1.26.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=14" + } + }, "node_modules/@types/node": { "version": "14.17.33", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.33.tgz", "integrity": "sha512-noEeJ06zbn3lOh4gqe2v7NMGS33jrulfNqYFDjjEbhpDEHR5VTxgYNQSBqBlJIsBJW3uEYDgD6kvMnrrhGzq8g==", "dev": true }, - "node_modules/@types/sinonjs__fake-timers": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.4.tgz", - "integrity": "sha512-IFQTJARgMUBF+xVd2b+hIgXWrZEjND3vJtRCvIelcFB5SIXfjV4bOHbHJ0eXKh+0COrBRc8MqteKAz/j88rE0A==", - "dev": true - }, - "node_modules/@types/sizzle": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.3.tgz", - "integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==", - "dev": true - }, - "node_modules/@types/yauzl": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.2.tgz", - "integrity": "sha512-8uALY5LTvSuHgloDVUvWP3pIauILm+8/0pDMokuDYIoNsOkSwd5AiHBTSEJjKTDcZr5z8UpgOWZkxBF4iJftoA==", - "dev": true, - "optional": true, + "node_modules/@umbraco/json-models-builders": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@umbraco/json-models-builders/-/json-models-builders-1.0.0.tgz", + "integrity": "sha512-UuJmA2S0xFuW2IT004K8U5Ek2sK9DJ0VZysPoeqdCN/aqk0Xi+EIBILFgk5xuSSSQDPUrWS7rjgnv6fawkFceg==", "dependencies": { - "@types/node": "*" + "camelize": "^1.0.0", + "faker": "^4.1.0" + } + }, + "node_modules/@umbraco/playwright-testhelpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@umbraco/playwright-testhelpers/-/playwright-testhelpers-1.0.1.tgz", + "integrity": "sha512-o3UnVpIlwd9KMKp5Hnv31cUBCkzzIagFY2quQsMFeVfaKXr7Ku1+3egArB9S3bwQhz3aan0jzlmwIp9D9r8vxg==", + "dependencies": { + "@umbraco/playwright-models": "^5.0.0", + "camelize": "^1.0.0", + "faker": "^4.1.0", + "form-data": "^4.0.0", + "node-fetch": "^2.6.7", + "xhr2": "^0.2.1" + } + }, + "node_modules/@umbraco/playwright-testhelpers/node_modules/@umbraco/playwright-models": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@umbraco/playwright-models/-/playwright-models-5.0.0.tgz", + "integrity": "sha512-HOf81JzlGysH9MoZTOH77jjHBEjveTMcxQRpyIfXfQmjdOar6nrEv5MPBMXwgiizLwnkhQBFkRuzKA/YASQnAg==", + "dependencies": { + "camelize": "^1.0.0", + "faker": "^4.1.0" } }, "node_modules/aggregate-error": { @@ -144,99 +135,6 @@ "node": ">=8" } }, - "node_modules/aggregate-error/node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/arch": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", - "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, "node_modules/array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", @@ -246,68 +144,16 @@ "node": ">=8" } }, - "node_modules/asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", - "dev": true, - "dependencies": { - "safer-buffer": "~2.1.0" - } - }, - "node_modules/assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/async": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.2.tgz", - "integrity": "sha512-H0E+qZaDEfx/FY4t7iLRv1W2fFI6+pyCeTw1uN20AQPiwqwM6ojPxHxdLv4z8hi2DtnW9BOckSspLucW7pIE5g==", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz", + "integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==", "dev": true }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true - }, - "node_modules/at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "dev": true, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/aws4": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", - "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", - "dev": true + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, "node_modules/balanced-match": { "version": "1.0.2", @@ -315,27 +161,6 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, - "node_modules/bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "dev": true, - "dependencies": { - "tweetnacl": "^0.14.3" - } - }, - "node_modules/blob-util": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/blob-util/-/blob-util-2.0.2.tgz", - "integrity": "sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==", - "dev": true - }, - "node_modules/bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "dev": true - }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -358,78 +183,10 @@ "node": ">=8" } }, - "node_modules/buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/cachedir": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.3.0.tgz", - "integrity": "sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/camelize": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz", - "integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs=", - "dev": true - }, - "node_modules/caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/chalk/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/check-more-types": { - "version": "2.24.0", - "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz", - "integrity": "sha1-FCD/sQ/URNz8ebQ4kbv//TKoRgA=", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/ci-info": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.2.0.tgz", - "integrity": "sha512-dVqRX7fLUm8J6FgHJ418XuIgDLZDkYcDFTeL6TA2gt5WlIZUQrrH6EZrNClwT/H0FateUsZkGIOPRrLbP+PR9A==", - "dev": true + "integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs=" }, "node_modules/clean-stack": { "version": "2.2.0", @@ -440,87 +197,10 @@ "node": ">=6" } }, - "node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dev": true, - "dependencies": { - "restore-cursor": "^3.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-table3": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.0.tgz", - "integrity": "sha512-gnB85c3MGC7Nm9I/FkiasNBOKjOiO1RNuXXarQms37q4QMpWdlbBgD/VnOStA2faG1dpXMv31RFApjX1/QdgWQ==", - "dev": true, - "dependencies": { - "object-assign": "^4.1.0", - "string-width": "^4.2.0" - }, - "engines": { - "node": "10.* || >= 12.*" - }, - "optionalDependencies": { - "colors": "^1.1.2" - } - }, - "node_modules/cli-truncate": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", - "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", - "dev": true, - "dependencies": { - "slice-ansi": "^3.0.0", - "string-width": "^4.2.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/clone": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", - "dev": true, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/colorette": { - "version": "2.0.16", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz", - "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==", - "dev": true - }, "node_modules/colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "integrity": "sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw==", "dev": true, "engines": { "node": ">=0.1.90" @@ -530,7 +210,6 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, "dependencies": { "delayed-stream": "~1.0.0" }, @@ -538,184 +217,25 @@ "node": ">= 0.8" } }, - "node_modules/commander": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", - "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/common-tags": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.1.tgz", - "integrity": "sha512-uOZd85rJqrdEIE/JjhW5YAeatX8iqjjvVzIyfx7JL7G5r9Tep6YpYT9gEJWhWpVyDQEyzukWd6p2qULpJ8tmBw==", - "dev": true, - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true - }, - "node_modules/cross-env": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", - "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.1" - }, - "bin": { - "cross-env": "src/bin/cross-env.js", - "cross-env-shell": "src/bin/cross-env-shell.js" - }, - "engines": { - "node": ">=10.14", - "npm": ">=6", - "yarn": ">=1" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/cy-verify-downloads": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/cy-verify-downloads/-/cy-verify-downloads-0.0.5.tgz", - "integrity": "sha512-aRK7VvKG5rmDJK4hjZ27KM2oOOz0cMO7z/j4zX8qCc4ffXZS1XRJkofUY0w5u6MCB/wUsNMs03VuvkeR2tNPoQ==", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, "node_modules/cycle": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", - "integrity": "sha1-IegLK+hYD5i0aPN5QwZisEbDStI=", + "integrity": "sha512-TVF6svNzeQCOpjCqsy0/CSy8VgObG3wXusJ73xW2GbG5rGx7lC8zxDSURicsXI2UsGdi2L0QNRCi745/wUDvsA==", "dev": true, "engines": { "node": ">=0.4.0" } }, - "node_modules/cypress": { - "version": "8.4.1", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-8.4.1.tgz", - "integrity": "sha512-itJXq0Vx3sXCUrDyBi2IUrkxVu/gTTp1VhjB5tzGgkeCR8Ae+/T8WV63rsZ7fS8Tpq7LPPXiyoM/sEdOX7cR6A==", - "dev": true, - "hasInstallScript": true, - "dependencies": { - "@cypress/request": "^2.88.6", - "@cypress/xvfb": "^1.2.4", - "@types/node": "^14.14.31", - "@types/sinonjs__fake-timers": "^6.0.2", - "@types/sizzle": "^2.3.2", - "arch": "^2.2.0", - "blob-util": "^2.0.2", - "bluebird": "^3.7.2", - "cachedir": "^2.3.0", - "chalk": "^4.1.0", - "check-more-types": "^2.24.0", - "cli-cursor": "^3.1.0", - "cli-table3": "~0.6.0", - "commander": "^5.1.0", - "common-tags": "^1.8.0", - "dayjs": "^1.10.4", - "debug": "^4.3.2", - "enquirer": "^2.3.6", - "eventemitter2": "^6.4.3", - "execa": "4.1.0", - "executable": "^4.1.1", - "extract-zip": "2.0.1", - "figures": "^3.2.0", - "fs-extra": "^9.1.0", - "getos": "^3.2.1", - "is-ci": "^3.0.0", - "is-installed-globally": "~0.4.0", - "lazy-ass": "^1.6.0", - "listr2": "^3.8.3", - "lodash": "^4.17.21", - "log-symbols": "^4.0.0", - "minimist": "^1.2.5", - "ospath": "^1.2.2", - "pretty-bytes": "^5.6.0", - "ramda": "~0.27.1", - "request-progress": "^3.0.0", - "supports-color": "^8.1.1", - "tmp": "~0.2.1", - "untildify": "^4.0.0", - "url": "^0.11.0", - "yauzl": "^2.10.0" - }, - "bin": { - "cypress": "bin/cypress" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "dev": true, - "dependencies": { - "assert-plus": "^1.0.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/dayjs": { - "version": "1.10.7", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.10.7.tgz", - "integrity": "sha512-P6twpd70BcPK34K26uJ1KT3wlhpuOAPoMwJzpsIWUxHZ7wpmbdZL/hQqBDfz7hGurYSa5PhzdhDHtt319hL3ig==", - "dev": true - }, - "node_modules/debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/debug/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, "node_modules/del": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/del/-/del-6.0.0.tgz", - "integrity": "sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/del/-/del-6.1.1.tgz", + "integrity": "sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg==", "dev": true, "dependencies": { "globby": "^11.0.1", @@ -738,7 +258,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true, "engines": { "node": ">=0.4.0" } @@ -755,132 +274,18 @@ "node": ">=8" } }, - "node_modules/ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "dev": true, - "dependencies": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "dependencies": { - "ansi-colors": "^4.1.1" - }, + "node_modules/dotenv": { + "version": "16.0.2", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.2.tgz", + "integrity": "sha512-JvpYKUmzQhYoIFgK2MOnF3bciIZoItIIoryihy0rIA+H4Jy0FmgyKYAHCTN98P5ybGSJcIFbh6QKeJdtZd1qhA==", "engines": { - "node": ">=8.6" + "node": ">=12" } }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/eventemitter2": { - "version": "6.4.5", - "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.5.tgz", - "integrity": "sha512-bXE7Dyc1i6oQElDG0jMRZJrRAn9QR2xyyFGmBdZleNmyQX0FqGYmhZIrIrpPfm/w//LTo4tVQGOGQcGCb5q9uw==", - "dev": true - }, - "node_modules/execa": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", - "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.0", - "get-stream": "^5.0.0", - "human-signals": "^1.1.1", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.0", - "onetime": "^5.1.0", - "signal-exit": "^3.0.2", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/executable": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz", - "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==", - "dev": true, - "dependencies": { - "pify": "^2.2.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true - }, - "node_modules/extract-zip": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", - "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", - "dev": true, - "dependencies": { - "debug": "^4.1.1", - "get-stream": "^5.1.0", - "yauzl": "^2.10.0" - }, - "bin": { - "extract-zip": "cli.js" - }, - "engines": { - "node": ">= 10.17.0" - }, - "optionalDependencies": { - "@types/yauzl": "^2.9.1" - } - }, - "node_modules/extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "dev": true, - "engines": [ - "node >=0.6.0" - ] - }, "node_modules/eyes": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", - "integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A=", + "integrity": "sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ==", "dev": true, "engines": { "node": "> 0.1.90" @@ -889,19 +294,12 @@ "node_modules/faker": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/faker/-/faker-4.1.0.tgz", - "integrity": "sha1-HkW7vsxndLPBlfrSg1EJxtdIzD8=", - "dev": true - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "integrity": "sha1-HkW7vsxndLPBlfrSg1EJxtdIzD8=" }, "node_modules/fast-glob": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", - "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -911,15 +309,9 @@ "micromatch": "^4.0.4" }, "engines": { - "node": ">=8" + "node": ">=8.6.0" } }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, "node_modules/fastq": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", @@ -929,30 +321,6 @@ "reusify": "^1.0.4" } }, - "node_modules/fd-slicer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", - "dev": true, - "dependencies": { - "pend": "~1.2.0" - } - }, - "node_modules/figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -965,93 +333,35 @@ "node": ">=8" } }, - "node_modules/forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true, - "engines": { - "node": "*" - } - }, "node_modules/form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", "dependencies": { "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", + "combined-stream": "^1.0.8", "mime-types": "^2.1.12" }, "engines": { - "node": ">= 0.12" - } - }, - "node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" + "node": ">= 6" } }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, - "node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/getos": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/getos/-/getos-3.2.1.tgz", - "integrity": "sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==", - "dev": true, - "dependencies": { - "async": "^3.2.0" - } - }, - "node_modules/getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, - "dependencies": { - "assert-plus": "^1.0.0" - } - }, "node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" }, @@ -1074,32 +384,17 @@ "node": ">= 6" } }, - "node_modules/global-dirs": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.0.tgz", - "integrity": "sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA==", - "dev": true, - "dependencies": { - "ini": "2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/globby": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", - "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", "slash": "^3.0.0" }, "engines": { @@ -1110,80 +405,33 @@ } }, "node_modules/graceful-fs": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", - "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", "dev": true }, - "node_modules/har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "deprecated": "this library is no longer supported", - "dev": true, - "dependencies": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dev": true, - "dependencies": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - }, - "engines": { - "node": ">=0.8", - "npm": ">=1.3.7" - } - }, - "node_modules/human-signals": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", - "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", - "dev": true, - "engines": { - "node": ">=8.12.0" - } - }, "node_modules/ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", "dev": true, "engines": { "node": ">= 4" } }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dev": true, "dependencies": { "once": "^1.3.0", @@ -1196,45 +444,15 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, - "node_modules/ini": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", - "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/is-ci": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", - "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", - "dev": true, - "dependencies": { - "ci-info": "^3.2.0" - }, - "bin": { - "is-ci": "bin.js" - } - }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -1247,22 +465,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-installed-globally": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", - "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", - "dev": true, - "dependencies": { - "global-dirs": "^3.0.0", - "is-path-inside": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -1290,211 +492,10 @@ "node": ">=8" } }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, "node_modules/isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "dev": true - }, - "node_modules/jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true - }, - "node_modules/json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "dev": true - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true - }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "dev": true, - "engines": [ - "node >=0.6.0" - ], - "dependencies": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, - "node_modules/lazy-ass": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz", - "integrity": "sha1-eZllXoZGwX8In90YfRUNMyTVRRM=", - "dev": true, - "engines": { - "node": "> 0.8" - } - }, - "node_modules/listr2": { - "version": "3.13.3", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-3.13.3.tgz", - "integrity": "sha512-VqAgN+XVfyaEjSaFewGPcDs5/3hBbWVaX1VgWv2f52MF7US45JuARlArULctiB44IIcEk3JF7GtoFCLqEdeuPA==", - "dev": true, - "dependencies": { - "cli-truncate": "^2.1.0", - "clone": "^2.1.2", - "colorette": "^2.0.16", - "log-update": "^4.0.0", - "p-map": "^4.0.0", - "rxjs": "^7.4.0", - "through": "^2.3.8", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "enquirer": ">= 2.3.0 < 3" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "node_modules/lodash.once": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=", - "dev": true - }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-update": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", - "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", - "dev": true, - "dependencies": { - "ansi-escapes": "^4.3.0", - "cli-cursor": "^3.1.0", - "slice-ansi": "^4.0.0", - "wrap-ansi": "^6.2.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-update/node_modules/slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/log-update/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", "dev": true }, "node_modules/merge2": { @@ -1507,13 +508,13 @@ } }, "node_modules/micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", "dev": true, "dependencies": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" + "braces": "^3.0.2", + "picomatch": "^2.3.1" }, "engines": { "node": ">=8.6" @@ -1523,7 +524,6 @@ "version": "1.51.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==", - "dev": true, "engines": { "node": ">= 0.6" } @@ -1532,7 +532,6 @@ "version": "2.1.34", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", - "dev": true, "dependencies": { "mime-db": "1.51.0" }, @@ -1540,19 +539,10 @@ "node": ">= 0.6" } }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "dependencies": { "brace-expansion": "^1.1.7" @@ -1561,18 +551,6 @@ "node": "*" } }, - "node_modules/minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", - "dev": true - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, "node_modules/mute-stream": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", @@ -1582,63 +560,40 @@ "node_modules/ncp": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", - "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=", + "integrity": "sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA==", "dev": true, "bin": { "ncp": "bin/ncp" } }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, + "node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", "dependencies": { - "path-key": "^3.0.0" + "whatwg-url": "^5.0.0" }, "engines": { - "node": ">=8" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true, - "engines": { - "node": ">=0.10.0" + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } } }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, "dependencies": { "wrappy": "1" } }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ospath": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/ospath/-/ospath-1.2.2.tgz", - "integrity": "sha1-EnZjl3Sj+O8lcvf+QoDg6kVQwHs=", - "dev": true - }, "node_modules/p-map": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", @@ -1657,21 +612,12 @@ "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -1681,22 +627,10 @@ "node": ">=8" } }, - "node_modules/pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", - "dev": true - }, - "node_modules/performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true - }, "node_modules/picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, "engines": { "node": ">=8.6" @@ -1705,91 +639,32 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "node_modules/playwright-core": { + "version": "1.26.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.26.0.tgz", + "integrity": "sha512-p8huU8eU4gD3VkJd3DA1nA7R3XA6rFvFL+1RYS96cSljCF2yJE9CWEHTPF4LqX8KN9MoWCrAfVKP5381X3CZqg==", "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pretty-bytes": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", - "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", - "dev": true, - "engines": { - "node": ">=6" + "bin": { + "playwright": "cli.js" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=14" } }, "node_modules/prompt": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/prompt/-/prompt-1.2.0.tgz", - "integrity": "sha512-iGerYRpRUg5ZyC+FJ/25G5PUKuWAGRjW1uOlhX7Pi3O5YygdK6R+KEaBjRbHSkU5vfS5PZCltSPZdDtUYwRCZA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/prompt/-/prompt-1.3.0.tgz", + "integrity": "sha512-ZkaRWtaLBZl7KKAKndKYUL8WqNT+cQHKRZnT4RYYms48jQkFw3rrBL+/N5K/KtdEveHkxs982MX2BkDKub2ZMg==", "dev": true, "dependencies": { - "async": "~0.9.0", - "colors": "^1.1.2", + "@colors/colors": "1.5.0", + "async": "3.2.3", "read": "1.0.x", "revalidator": "0.1.x", "winston": "2.x" }, "engines": { - "node": ">= 0.6.6" - } - }, - "node_modules/prompt/node_modules/async": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", - "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=", - "dev": true - }, - "node_modules/psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", - "dev": true - }, - "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", - "dev": true, - "engines": { - "node": ">=0.6" - } - }, - "node_modules/querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", - "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", - "dev": true, - "engines": { - "node": ">=0.4.x" + "node": ">= 6.0.0" } }, "node_modules/queue-microtask": { @@ -1812,16 +687,10 @@ } ] }, - "node_modules/ramda": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.27.1.tgz", - "integrity": "sha512-PgIdVpn5y5Yns8vqb8FzBUEYn98V3xcPgawAkkgj0YJ0qDsnHCiNmZYfOGMgOvoB0eWFLpYbhxUR3mxfDIMvpw==", - "dev": true - }, "node_modules/read": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", - "integrity": "sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ=", + "integrity": "sha512-rSOKNYUmaxy0om1BNjMN4ezNT6VKK+2xF4GBhc81mkH7L60i6dp8qPYrkndNLT3QPphoII3maL9PVC9XmhHwVQ==", "dev": true, "dependencies": { "mute-stream": "~0.0.4" @@ -1830,28 +699,6 @@ "node": ">=0.8" } }, - "node_modules/request-progress": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz", - "integrity": "sha1-TKdUCBx/7GP1BeT6qCWqBs1mnb4=", - "dev": true, - "dependencies": { - "throttleit": "^1.0.0" - } - }, - "node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dev": true, - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -1865,7 +712,7 @@ "node_modules/revalidator": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/revalidator/-/revalidator-0.1.8.tgz", - "integrity": "sha1-/s5hv6DBtSoga9axgZgYS91SOjs=", + "integrity": "sha512-xcBILK2pA9oh4SiinPEZfhP8HfrB/ha+a2fTMyl7Om2WjlDVrOQy99N2MXXlUHqGJz4qEu2duXxHJjDWuK/0xg==", "dev": true, "engines": { "node": ">= 0.4.0" @@ -1909,68 +756,6 @@ "queue-microtask": "^1.2.2" } }, - "node_modules/rxjs": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.4.0.tgz", - "integrity": "sha512-7SQDi7xeTMCJpqViXh8gL/lebcwlp3d831F05+9B44A4B0WfsEwUQHR64gsH1kvJ+Ep/J9K2+n1hVl1CsGN23w==", - "dev": true, - "dependencies": { - "tslib": "~2.1.0" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/signal-exit": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz", - "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==", - "dev": true - }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -1980,128 +765,15 @@ "node": ">=8" } }, - "node_modules/slice-ansi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", - "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "dev": true, - "dependencies": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - }, - "bin": { - "sshpk-conv": "bin/sshpk-conv", - "sshpk-sign": "bin/sshpk-sign", - "sshpk-verify": "bin/sshpk-verify" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/stack-trace": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", "dev": true, "engines": { "node": "*" } }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/throttleit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz", - "integrity": "sha1-nnhYNtr0Z0MUWlmEtiaNgoUorGw=", - "dev": true - }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true - }, - "node_modules/tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", - "dev": true, - "dependencies": { - "rimraf": "^3.0.0" - }, - "engines": { - "node": ">=8.17.0" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -2114,59 +786,22 @@ "node": ">=8.0" } }, - "node_modules/tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dev": true, - "dependencies": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=0.8" - } + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, "node_modules/tslib": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", - "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", "dev": true }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, - "node_modules/tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "dev": true - }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/typescript": { - "version": "3.9.10", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz", - "integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==", + "version": "4.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.3.tgz", + "integrity": "sha512-goMHfm00nWPa8UvR/CPSvykqf6dVV8x/dp0c5mFTMTIu0u0FlGWRioyy7Nn0PGAdHxpJZnuO/ut+PpQ8UiHAig==", + "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -2175,110 +810,27 @@ "node": ">=4.2.0" } }, - "node_modules/umbraco-cypress-testhelpers": { - "version": "1.0.0-beta-73", - "resolved": "https://registry.npmjs.org/umbraco-cypress-testhelpers/-/umbraco-cypress-testhelpers-1.0.0-beta-73.tgz", - "integrity": "sha512-VZy7QFjY5o1oTWdpYGb9xrwr4qUw5BcbEwz0GYZexiKCr+Vqq3MllmLMWfkRl4/9O/tbu+ggKx3OZ49GRAGUyg==", - "dev": true, - "hasInstallScript": true, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "dependencies": { - "camelize": "^1.0.0", - "faker": "^4.1.0" - }, - "peerDependencies": { - "cross-env": "^7.0.2", - "cypress": "^8.0.0", - "ncp": "^2.0.0" - } - }, - "node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "dev": true, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/untildify": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", - "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/url": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", - "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", - "dev": true, - "dependencies": { - "punycode": "1.3.2", - "querystring": "0.2.0" - } - }, - "node_modules/url/node_modules/punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", - "dev": true - }, - "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true, - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "dev": true, - "engines": [ - "node >=0.6.0" - ], - "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" } }, "node_modules/winston": { - "version": "2.4.5", - "resolved": "https://registry.npmjs.org/winston/-/winston-2.4.5.tgz", - "integrity": "sha512-TWoamHt5yYvsMarGlGEQE59SbJHqGsZV8/lwC+iCcGeAe0vUaOh+Lv6SYM17ouzC/a/LB1/hz/7sxFBtlu1l4A==", + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/winston/-/winston-2.4.6.tgz", + "integrity": "sha512-J5Zu4p0tojLde8mIOyDSsmLmcP8I3Z6wtwpTDHx1+hGcdhxcJaAmG4CFtagkb+NiN1M9Ek4b42pzMWqfc9jm8w==", "dev": true, "dependencies": { - "async": "~1.0.0", + "async": "^3.2.3", "colors": "1.0.x", "cycle": "1.0.x", "eyes": "0.1.x", @@ -2289,103 +841,27 @@ "node": ">= 0.10.0" } }, - "node_modules/winston/node_modules/async": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz", - "integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k=", - "dev": true - }, - "node_modules/winston/node_modules/colors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", - "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", - "dev": true, - "engines": { - "node": ">=0.1.90" - } - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, - "node_modules/yauzl": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", - "dev": true, - "dependencies": { - "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.1.0" + "node_modules/xhr2": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/xhr2/-/xhr2-0.2.1.tgz", + "integrity": "sha512-sID0rrVCqkVNUn8t6xuv9+6FViXjUVXq8H5rWOH2rz9fDNQEd4g0EA2XlcEdJXRz5BMEn4O1pJFdT+z4YHhoWw==", + "engines": { + "node": ">= 6" } } }, "dependencies": { - "@cypress/request": { - "version": "2.88.7", - "resolved": "https://registry.npmjs.org/@cypress/request/-/request-2.88.7.tgz", - "integrity": "sha512-FTULIP2rnDJvZDT9t6B4nSfYR40ue19tVmv3wUcY05R9/FPCoMl1nAPJkzWzBCo7ltVn5ThQTbxiMoGBN7k0ig==", - "dev": true, - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^8.3.2" - } - }, - "@cypress/xvfb": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@cypress/xvfb/-/xvfb-1.2.4.tgz", - "integrity": "sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==", - "dev": true, - "requires": { - "debug": "^3.1.0", - "lodash.once": "^4.1.1" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - } - } + "@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true }, "@nodelib/fs.scandir": { "version": "2.1.5", @@ -2413,32 +889,53 @@ "fastq": "^1.6.0" } }, + "@playwright/test": { + "version": "1.26.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.26.0.tgz", + "integrity": "sha512-D24pu1k/gQw3Lhbpc38G5bXlBjGDrH5A52MsrH12wz6ohGDeQ+aZg/JFSEsT/B3G8zlJe/EU4EkJK74hpqsjEg==", + "dev": true, + "requires": { + "@types/node": "*", + "playwright-core": "1.26.0" + } + }, "@types/node": { "version": "14.17.33", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.33.tgz", "integrity": "sha512-noEeJ06zbn3lOh4gqe2v7NMGS33jrulfNqYFDjjEbhpDEHR5VTxgYNQSBqBlJIsBJW3uEYDgD6kvMnrrhGzq8g==", "dev": true }, - "@types/sinonjs__fake-timers": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.4.tgz", - "integrity": "sha512-IFQTJARgMUBF+xVd2b+hIgXWrZEjND3vJtRCvIelcFB5SIXfjV4bOHbHJ0eXKh+0COrBRc8MqteKAz/j88rE0A==", - "dev": true - }, - "@types/sizzle": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.3.tgz", - "integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==", - "dev": true - }, - "@types/yauzl": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.2.tgz", - "integrity": "sha512-8uALY5LTvSuHgloDVUvWP3pIauILm+8/0pDMokuDYIoNsOkSwd5AiHBTSEJjKTDcZr5z8UpgOWZkxBF4iJftoA==", - "dev": true, - "optional": true, + "@umbraco/json-models-builders": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@umbraco/json-models-builders/-/json-models-builders-1.0.0.tgz", + "integrity": "sha512-UuJmA2S0xFuW2IT004K8U5Ek2sK9DJ0VZysPoeqdCN/aqk0Xi+EIBILFgk5xuSSSQDPUrWS7rjgnv6fawkFceg==", "requires": { - "@types/node": "*" + "camelize": "^1.0.0", + "faker": "^4.1.0" + } + }, + "@umbraco/playwright-testhelpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@umbraco/playwright-testhelpers/-/playwright-testhelpers-1.0.1.tgz", + "integrity": "sha512-o3UnVpIlwd9KMKp5Hnv31cUBCkzzIagFY2quQsMFeVfaKXr7Ku1+3egArB9S3bwQhz3aan0jzlmwIp9D9r8vxg==", + "requires": { + "@umbraco/playwright-models": "^5.0.0", + "camelize": "^1.0.0", + "faker": "^4.1.0", + "form-data": "^4.0.0", + "node-fetch": "^2.6.7", + "xhr2": "^0.2.1" + }, + "dependencies": { + "@umbraco/playwright-models": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@umbraco/playwright-models/-/playwright-models-5.0.0.tgz", + "integrity": "sha512-HOf81JzlGysH9MoZTOH77jjHBEjveTMcxQRpyIfXfQmjdOar6nrEv5MPBMXwgiizLwnkhQBFkRuzKA/YASQnAg==", + "requires": { + "camelize": "^1.0.0", + "faker": "^4.1.0" + } + } } }, "aggregate-error": { @@ -2449,120 +946,24 @@ "requires": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" - }, - "dependencies": { - "indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true - } } }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true - }, - "ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "requires": { - "type-fest": "^0.21.3" - } - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "arch": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", - "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", - "dev": true - }, "array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true }, - "asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", - "dev": true, - "requires": { - "safer-buffer": "~2.1.0" - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - }, - "astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true - }, "async": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.2.tgz", - "integrity": "sha512-H0E+qZaDEfx/FY4t7iLRv1W2fFI6+pyCeTw1uN20AQPiwqwM6ojPxHxdLv4z8hi2DtnW9BOckSspLucW7pIE5g==", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz", + "integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==", "dev": true }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true - }, - "at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "dev": true - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true - }, - "aws4": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", - "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", - "dev": true + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, "balanced-match": { "version": "1.0.2", @@ -2570,27 +971,6 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "dev": true, - "requires": { - "tweetnacl": "^0.14.3" - } - }, - "blob-util": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/blob-util/-/blob-util-2.0.2.tgz", - "integrity": "sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==", - "dev": true - }, - "bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "dev": true - }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -2610,62 +990,10 @@ "fill-range": "^7.0.1" } }, - "buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", - "dev": true - }, - "cachedir": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.3.0.tgz", - "integrity": "sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw==", - "dev": true - }, "camelize": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz", - "integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs=", - "dev": true - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "dependencies": { - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "check-more-types": { - "version": "2.24.0", - "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz", - "integrity": "sha1-FCD/sQ/URNz8ebQ4kbv//TKoRgA=", - "dev": true - }, - "ci-info": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.2.0.tgz", - "integrity": "sha512-dVqRX7fLUm8J6FgHJ418XuIgDLZDkYcDFTeL6TA2gt5WlIZUQrrH6EZrNClwT/H0FateUsZkGIOPRrLbP+PR9A==", - "dev": true + "integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs=" }, "clean-stack": { "version": "2.2.0", @@ -2673,219 +1001,36 @@ "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", "dev": true }, - "cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dev": true, - "requires": { - "restore-cursor": "^3.1.0" - } - }, - "cli-table3": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.0.tgz", - "integrity": "sha512-gnB85c3MGC7Nm9I/FkiasNBOKjOiO1RNuXXarQms37q4QMpWdlbBgD/VnOStA2faG1dpXMv31RFApjX1/QdgWQ==", - "dev": true, - "requires": { - "colors": "^1.1.2", - "object-assign": "^4.1.0", - "string-width": "^4.2.0" - } - }, - "cli-truncate": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", - "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", - "dev": true, - "requires": { - "slice-ansi": "^3.0.0", - "string-width": "^4.2.0" - } - }, - "clone": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", - "dev": true - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "colorette": { - "version": "2.0.16", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz", - "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==", - "dev": true - }, "colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "integrity": "sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw==", "dev": true }, "combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, "requires": { "delayed-stream": "~1.0.0" } }, - "commander": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", - "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", - "dev": true - }, - "common-tags": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.1.tgz", - "integrity": "sha512-uOZd85rJqrdEIE/JjhW5YAeatX8iqjjvVzIyfx7JL7G5r9Tep6YpYT9gEJWhWpVyDQEyzukWd6p2qULpJ8tmBw==", - "dev": true - }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true - }, - "cross-env": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", - "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.1" - } - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "cy-verify-downloads": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/cy-verify-downloads/-/cy-verify-downloads-0.0.5.tgz", - "integrity": "sha512-aRK7VvKG5rmDJK4hjZ27KM2oOOz0cMO7z/j4zX8qCc4ffXZS1XRJkofUY0w5u6MCB/wUsNMs03VuvkeR2tNPoQ==", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, "cycle": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", - "integrity": "sha1-IegLK+hYD5i0aPN5QwZisEbDStI=", + "integrity": "sha512-TVF6svNzeQCOpjCqsy0/CSy8VgObG3wXusJ73xW2GbG5rGx7lC8zxDSURicsXI2UsGdi2L0QNRCi745/wUDvsA==", "dev": true }, - "cypress": { - "version": "8.4.1", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-8.4.1.tgz", - "integrity": "sha512-itJXq0Vx3sXCUrDyBi2IUrkxVu/gTTp1VhjB5tzGgkeCR8Ae+/T8WV63rsZ7fS8Tpq7LPPXiyoM/sEdOX7cR6A==", - "dev": true, - "requires": { - "@cypress/request": "^2.88.6", - "@cypress/xvfb": "^1.2.4", - "@types/node": "^14.14.31", - "@types/sinonjs__fake-timers": "^6.0.2", - "@types/sizzle": "^2.3.2", - "arch": "^2.2.0", - "blob-util": "^2.0.2", - "bluebird": "^3.7.2", - "cachedir": "^2.3.0", - "chalk": "^4.1.0", - "check-more-types": "^2.24.0", - "cli-cursor": "^3.1.0", - "cli-table3": "~0.6.0", - "commander": "^5.1.0", - "common-tags": "^1.8.0", - "dayjs": "^1.10.4", - "debug": "^4.3.2", - "enquirer": "^2.3.6", - "eventemitter2": "^6.4.3", - "execa": "4.1.0", - "executable": "^4.1.1", - "extract-zip": "2.0.1", - "figures": "^3.2.0", - "fs-extra": "^9.1.0", - "getos": "^3.2.1", - "is-ci": "^3.0.0", - "is-installed-globally": "~0.4.0", - "lazy-ass": "^1.6.0", - "listr2": "^3.8.3", - "lodash": "^4.17.21", - "log-symbols": "^4.0.0", - "minimist": "^1.2.5", - "ospath": "^1.2.2", - "pretty-bytes": "^5.6.0", - "ramda": "~0.27.1", - "request-progress": "^3.0.0", - "supports-color": "^8.1.1", - "tmp": "~0.2.1", - "untildify": "^4.0.0", - "url": "^0.11.0", - "yauzl": "^2.10.0" - } - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, - "dayjs": { - "version": "1.10.7", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.10.7.tgz", - "integrity": "sha512-P6twpd70BcPK34K26uJ1KT3wlhpuOAPoMwJzpsIWUxHZ7wpmbdZL/hQqBDfz7hGurYSa5PhzdhDHtt319hL3ig==", - "dev": true - }, - "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "dev": true, - "requires": { - "ms": "2.1.2" - }, - "dependencies": { - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, "del": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/del/-/del-6.0.0.tgz", - "integrity": "sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/del/-/del-6.1.1.tgz", + "integrity": "sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg==", "dev": true, "requires": { "globby": "^11.0.1", @@ -2901,8 +1046,7 @@ "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" }, "dir-glob": { "version": "3.0.1", @@ -2913,124 +1057,26 @@ "path-type": "^4.0.0" } }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "dev": true, - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, - "requires": { - "once": "^1.4.0" - } - }, - "enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "requires": { - "ansi-colors": "^4.1.1" - } - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "eventemitter2": { - "version": "6.4.5", - "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.5.tgz", - "integrity": "sha512-bXE7Dyc1i6oQElDG0jMRZJrRAn9QR2xyyFGmBdZleNmyQX0FqGYmhZIrIrpPfm/w//LTo4tVQGOGQcGCb5q9uw==", - "dev": true - }, - "execa": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", - "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.0", - "get-stream": "^5.0.0", - "human-signals": "^1.1.1", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.0", - "onetime": "^5.1.0", - "signal-exit": "^3.0.2", - "strip-final-newline": "^2.0.0" - } - }, - "executable": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz", - "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==", - "dev": true, - "requires": { - "pify": "^2.2.0" - } - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true - }, - "extract-zip": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", - "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", - "dev": true, - "requires": { - "@types/yauzl": "^2.9.1", - "debug": "^4.1.1", - "get-stream": "^5.1.0", - "yauzl": "^2.10.0" - } - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "dev": true + "dotenv": { + "version": "16.0.2", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.2.tgz", + "integrity": "sha512-JvpYKUmzQhYoIFgK2MOnF3bciIZoItIIoryihy0rIA+H4Jy0FmgyKYAHCTN98P5ybGSJcIFbh6QKeJdtZd1qhA==" }, "eyes": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", - "integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A=", + "integrity": "sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ==", "dev": true }, "faker": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/faker/-/faker-4.1.0.tgz", - "integrity": "sha1-HkW7vsxndLPBlfrSg1EJxtdIzD8=", - "dev": true - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "integrity": "sha1-HkW7vsxndLPBlfrSg1EJxtdIzD8=" }, "fast-glob": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", - "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", @@ -3040,12 +1086,6 @@ "micromatch": "^4.0.4" } }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, "fastq": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", @@ -3055,24 +1095,6 @@ "reusify": "^1.0.4" } }, - "fd-slicer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", - "dev": true, - "requires": { - "pend": "~1.2.0" - } - }, - "figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -3082,78 +1104,32 @@ "to-regex-range": "^5.0.1" } }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true - }, "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", "requires": { "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", + "combined-stream": "^1.0.8", "mime-types": "^2.1.12" } }, - "fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, - "requires": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, - "get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "getos": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/getos/-/getos-3.2.1.tgz", - "integrity": "sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==", - "dev": true, - "requires": { - "async": "^3.2.0" - } - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } @@ -3167,84 +1143,42 @@ "is-glob": "^4.0.1" } }, - "global-dirs": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.0.tgz", - "integrity": "sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA==", - "dev": true, - "requires": { - "ini": "2.0.0" - } - }, "globby": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", - "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, "requires": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", "slash": "^3.0.0" } }, "graceful-fs": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", - "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", - "dev": true - }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true - }, - "har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "dev": true, - "requires": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, - "human-signals": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", - "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", "dev": true }, "ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true + }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "dev": true }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dev": true, "requires": { "once": "^1.3.0", @@ -3257,31 +1191,10 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, - "ini": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", - "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", - "dev": true - }, - "is-ci": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", - "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", - "dev": true, - "requires": { - "ci-info": "^3.2.0" - } - }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true }, "is-glob": { @@ -3293,16 +1206,6 @@ "is-extglob": "^2.1.1" } }, - "is-installed-globally": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", - "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", - "dev": true, - "requires": { - "global-dirs": "^3.0.0", - "is-path-inside": "^3.0.2" - } - }, "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -3321,166 +1224,10 @@ "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "dev": true }, - "is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, - "is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "dev": true - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true - }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true - }, - "jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^2.0.0" - } - }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "dev": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, - "lazy-ass": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz", - "integrity": "sha1-eZllXoZGwX8In90YfRUNMyTVRRM=", - "dev": true - }, - "listr2": { - "version": "3.13.3", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-3.13.3.tgz", - "integrity": "sha512-VqAgN+XVfyaEjSaFewGPcDs5/3hBbWVaX1VgWv2f52MF7US45JuARlArULctiB44IIcEk3JF7GtoFCLqEdeuPA==", - "dev": true, - "requires": { - "cli-truncate": "^2.1.0", - "clone": "^2.1.2", - "colorette": "^2.0.16", - "log-update": "^4.0.0", - "p-map": "^4.0.0", - "rxjs": "^7.4.0", - "through": "^2.3.8", - "wrap-ansi": "^7.0.0" - } - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "lodash.once": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=", - "dev": true - }, - "log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "requires": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - } - }, - "log-update": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", - "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", - "dev": true, - "requires": { - "ansi-escapes": "^4.3.0", - "cli-cursor": "^3.1.0", - "slice-ansi": "^4.0.0", - "wrap-ansi": "^6.2.0" - }, - "dependencies": { - "slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - } - }, - "wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - } - } - }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", "dev": true }, "merge2": { @@ -3490,57 +1237,37 @@ "dev": true }, "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", "dev": true, "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" + "braces": "^3.0.2", + "picomatch": "^2.3.1" } }, "mime-db": { "version": "1.51.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", - "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==", - "dev": true + "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==" }, "mime-types": { "version": "2.1.34", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", - "dev": true, "requires": { "mime-db": "1.51.0" } }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "requires": { "brace-expansion": "^1.1.7" } }, - "minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", - "dev": true - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, "mute-stream": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", @@ -3550,48 +1277,26 @@ "ncp": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", - "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=", + "integrity": "sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA==", "dev": true }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, + "node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", "requires": { - "path-key": "^3.0.0" + "whatwg-url": "^5.0.0" } }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, "requires": { "wrappy": "1" } }, - "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "ospath": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/ospath/-/ospath-1.2.2.tgz", - "integrity": "sha1-EnZjl3Sj+O8lcvf+QoDg6kVQwHs=", - "dev": true - }, "p-map": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", @@ -3604,13 +1309,7 @@ "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true }, "path-type": { @@ -3619,131 +1318,46 @@ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true }, - "pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", - "dev": true - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true - }, "picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - }, - "pretty-bytes": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", - "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", + "playwright-core": { + "version": "1.26.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.26.0.tgz", + "integrity": "sha512-p8huU8eU4gD3VkJd3DA1nA7R3XA6rFvFL+1RYS96cSljCF2yJE9CWEHTPF4LqX8KN9MoWCrAfVKP5381X3CZqg==", "dev": true }, "prompt": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/prompt/-/prompt-1.2.0.tgz", - "integrity": "sha512-iGerYRpRUg5ZyC+FJ/25G5PUKuWAGRjW1uOlhX7Pi3O5YygdK6R+KEaBjRbHSkU5vfS5PZCltSPZdDtUYwRCZA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/prompt/-/prompt-1.3.0.tgz", + "integrity": "sha512-ZkaRWtaLBZl7KKAKndKYUL8WqNT+cQHKRZnT4RYYms48jQkFw3rrBL+/N5K/KtdEveHkxs982MX2BkDKub2ZMg==", "dev": true, "requires": { - "async": "~0.9.0", - "colors": "^1.1.2", + "@colors/colors": "1.5.0", + "async": "3.2.3", "read": "1.0.x", "revalidator": "0.1.x", "winston": "2.x" - }, - "dependencies": { - "async": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", - "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=", - "dev": true - } } }, - "psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", - "dev": true - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true - }, - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", - "dev": true - }, - "querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", - "dev": true - }, "queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true }, - "ramda": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.27.1.tgz", - "integrity": "sha512-PgIdVpn5y5Yns8vqb8FzBUEYn98V3xcPgawAkkgj0YJ0qDsnHCiNmZYfOGMgOvoB0eWFLpYbhxUR3mxfDIMvpw==", - "dev": true - }, "read": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", - "integrity": "sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ=", + "integrity": "sha512-rSOKNYUmaxy0om1BNjMN4ezNT6VKK+2xF4GBhc81mkH7L60i6dp8qPYrkndNLT3QPphoII3maL9PVC9XmhHwVQ==", "dev": true, "requires": { "mute-stream": "~0.0.4" } }, - "request-progress": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz", - "integrity": "sha1-TKdUCBx/7GP1BeT6qCWqBs1mnb4=", - "dev": true, - "requires": { - "throttleit": "^1.0.0" - } - }, - "restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dev": true, - "requires": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - } - }, "reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -3753,7 +1367,7 @@ "revalidator": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/revalidator/-/revalidator-0.1.8.tgz", - "integrity": "sha1-/s5hv6DBtSoga9axgZgYS91SOjs=", + "integrity": "sha512-xcBILK2pA9oh4SiinPEZfhP8HfrB/ha+a2fTMyl7Om2WjlDVrOQy99N2MXXlUHqGJz4qEu2duXxHJjDWuK/0xg==", "dev": true }, "rimraf": { @@ -3774,144 +1388,18 @@ "queue-microtask": "^1.2.2" } }, - "rxjs": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.4.0.tgz", - "integrity": "sha512-7SQDi7xeTMCJpqViXh8gL/lebcwlp3d831F05+9B44A4B0WfsEwUQHR64gsH1kvJ+Ep/J9K2+n1hVl1CsGN23w==", - "dev": true, - "requires": { - "tslib": "~2.1.0" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "signal-exit": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz", - "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==", - "dev": true - }, "slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true }, - "slice-ansi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", - "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - } - }, - "sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "dev": true, - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - } - }, "stack-trace": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", "dev": true }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true - }, - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "throttleit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz", - "integrity": "sha1-nnhYNtr0Z0MUWlmEtiaNgoUorGw=", - "dev": true - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true - }, - "tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", - "dev": true, - "requires": { - "rimraf": "^3.0.0" - } - }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -3921,177 +1409,61 @@ "is-number": "^7.0.0" } }, - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dev": true, - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - } + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, "tslib": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", - "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==", - "dev": true - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "dev": true - }, - "type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", "dev": true }, "typescript": { - "version": "3.9.10", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz", - "integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==" - }, - "umbraco-cypress-testhelpers": { - "version": "1.0.0-beta-73", - "resolved": "https://registry.npmjs.org/umbraco-cypress-testhelpers/-/umbraco-cypress-testhelpers-1.0.0-beta-73.tgz", - "integrity": "sha512-VZy7QFjY5o1oTWdpYGb9xrwr4qUw5BcbEwz0GYZexiKCr+Vqq3MllmLMWfkRl4/9O/tbu+ggKx3OZ49GRAGUyg==", - "dev": true, - "requires": { - "camelize": "^1.0.0", - "faker": "^4.1.0" - } - }, - "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "version": "4.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.3.tgz", + "integrity": "sha512-goMHfm00nWPa8UvR/CPSvykqf6dVV8x/dp0c5mFTMTIu0u0FlGWRioyy7Nn0PGAdHxpJZnuO/ut+PpQ8UiHAig==", "dev": true }, - "untildify": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", - "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", - "dev": true + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "requires": { - "punycode": "^2.1.0" - } - }, - "url": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", - "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", - "dev": true, - "requires": { - "punycode": "1.3.2", - "querystring": "0.2.0" - }, - "dependencies": { - "punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", - "dev": true - } - } - }, - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true - }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" } }, "winston": { - "version": "2.4.5", - "resolved": "https://registry.npmjs.org/winston/-/winston-2.4.5.tgz", - "integrity": "sha512-TWoamHt5yYvsMarGlGEQE59SbJHqGsZV8/lwC+iCcGeAe0vUaOh+Lv6SYM17ouzC/a/LB1/hz/7sxFBtlu1l4A==", + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/winston/-/winston-2.4.6.tgz", + "integrity": "sha512-J5Zu4p0tojLde8mIOyDSsmLmcP8I3Z6wtwpTDHx1+hGcdhxcJaAmG4CFtagkb+NiN1M9Ek4b42pzMWqfc9jm8w==", "dev": true, "requires": { - "async": "~1.0.0", + "async": "^3.2.3", "colors": "1.0.x", "cycle": "1.0.x", "eyes": "0.1.x", "isstream": "0.1.x", "stack-trace": "0.0.x" - }, - "dependencies": { - "async": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz", - "integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k=", - "dev": true - }, - "colors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", - "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", - "dev": true - } - } - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" } }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, - "yauzl": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", - "dev": true, - "requires": { - "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.1.0" - } + "xhr2": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/xhr2/-/xhr2-0.2.1.tgz", + "integrity": "sha512-sID0rrVCqkVNUn8t6xuv9+6FViXjUVXq8H5rWOH2rz9fDNQEd4g0EA2XlcEdJXRz5BMEn4O1pJFdT+z4YHhoWw==" } } } diff --git a/tests/Umbraco.Tests.AcceptanceTest/package.json b/tests/Umbraco.Tests.AcceptanceTest/package.json index 45ef9bef67..4c68965f51 100644 --- a/tests/Umbraco.Tests.AcceptanceTest/package.json +++ b/tests/Umbraco.Tests.AcceptanceTest/package.json @@ -4,19 +4,26 @@ "scripts": { "postinstall": "node postinstall.js", "config": "node config.js", - "test": "npx cypress run", - "ui": "npx cypress open" + "ui": "npx playwright test tests/DefaultConfig", + "headed": "npx playwright test --headed tests/DefaultConfig", + "all": "npx playwright test" }, "devDependencies": { - "cross-env": "^7.0.2", - "cypress": "8.4.1", - "cy-verify-downloads": "0.0.5", + "@playwright/test": "^1.19.2", + "typescript": "^4.8.3", + "tslib": "^2.4.0", "del": "^6.0.0", "ncp": "^2.0.0", - "prompt": "^1.2.0", - "umbraco-cypress-testhelpers": "^1.0.0-beta-73" + "prompt": "^1.2.0" }, "dependencies": { - "typescript": "^3.9.2" + "@umbraco/json-models-builders": "^1.0.0", + "@umbraco/playwright-testhelpers": "^1.0.1", + "camelize": "^1.0.0", + "faker": "^4.1.0", + "form-data": "^4.0.0", + "node-fetch": "^2.6.7", + "xhr2": "^0.2.1", + "dotenv": "^16.0.2" } } diff --git a/tests/Umbraco.Tests.AcceptanceTest/playwright.config.ts b/tests/Umbraco.Tests.AcceptanceTest/playwright.config.ts new file mode 100644 index 0000000000..9f9a1a226b --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/playwright.config.ts @@ -0,0 +1,105 @@ +import type { PlaywrightTestConfig } from '@playwright/test'; +import { devices } from '@playwright/test'; +import dotenv from 'dotenv'; + +dotenv.config(); + +/** + * See https://playwright.dev/docs/test-configuration. + */ +const config: PlaywrightTestConfig = { + testDir: './tests/', + /* Maximum time one test can run for. */ + timeout: 30 * 1000, + expect: { + /** + * Maximum time expect() should wait for the condition to be met. + * For example in `await expect(locator).toHaveText();` + */ + timeout: 5000 + }, + /* Fail the build on CI if you accidentally left test.only in the source code. */ + forbidOnly: !!process.env.CI, + /* Retry on CI only */ + retries: process.env.CI ? 2 : 5, + // We don't want to run parallel, as tests might differ in state + workers: 1, + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + reporter: process.env.CI ? 'html' : 'line', + outputDir : "./results", + + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + use: { + /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */ + actionTimeout: 0, + /* Base URL to use in actions like `await page.goto('/')`. */ + // baseURL: 'http://localhost:44332', + + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: 'on-first-retry', + ignoreHTTPSErrors: true, + }, + + /* Configure projects for major browsers */ + projects: [ + { + name: 'chromium', + use: { + ...devices['Desktop Chrome'], + }, + }, + + // { + // name: 'firefox', + // use: { + // ...devices['Desktop Firefox'], + // }, + // }, + + // { + // name: 'webkit', + // use: { + // ...devices['Desktop Safari'], + // }, + // }, + + /* Test against mobile viewports. */ + // { + // name: 'Mobile Chrome', + // use: { + // ...devices['Pixel 5'], + // }, + // }, + // { + // name: 'Mobile Safari', + // use: { + // ...devices['iPhone 12'], + // }, + // }, + + /* Test against branded browsers. */ + // { + // name: 'Microsoft Edge', + // use: { + // channel: 'msedge', + // }, + // }, + // { + // name: 'Google Chrome', + // use: { + // channel: 'chrome', + // }, + // }, + ], + + /* Folder for test artifacts such as screenshots, videos, traces, etc. */ + // outputDir: 'test-results/', + + /* Run your local dev server before starting the tests */ + // webServer: { + // command: 'npm run start', + // port: 3000, + // }, +}; + +export default config; diff --git a/tests/Umbraco.Tests.AcceptanceTest/postinstall.js b/tests/Umbraco.Tests.AcceptanceTest/postinstall.js index 6117ac84f0..dd2dcaba31 100644 --- a/tests/Umbraco.Tests.AcceptanceTest/postinstall.js +++ b/tests/Umbraco.Tests.AcceptanceTest/postinstall.js @@ -1,6 +1,6 @@ const fs = require('fs'); -const configPath = './cypress.env.json'; +const configPath = './.env'; try { if (fs.existsSync(configPath)) { diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/AllowEditInvariantFromNonDefault=False/allowEditInvariantFromNonDefault=False.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/AllowEditInvariantFromNonDefault=False/allowEditInvariantFromNonDefault=False.spec.ts new file mode 100644 index 0000000000..d4404cbb3e --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/AllowEditInvariantFromNonDefault=False/allowEditInvariantFromNonDefault=False.spec.ts @@ -0,0 +1,123 @@ +import {AliasHelper, ApiHelpers, test} from '@umbraco/playwright-testhelpers'; +import {expect} from "@playwright/test"; +import {ContentBuilder, DocumentTypeBuilder, MacroBuilder, PartialViewMacroBuilder} from "@umbraco/playwright-models"; + +test.describe('Test for AllowEditInvariantFromNonDefault=False', () => { + const rootDocTypeName = 'TestDocument'; + const languageEn = 'en-US'; + const languageDa = 'da'; + + test.beforeEach(async ({page, umbracoApi}) => { + await umbracoApi.login(); + await umbracoApi.content.deleteAllContent(); + await umbracoApi.documentTypes.ensureNameNotExists(rootDocTypeName); + await umbracoApi.languages.ensureCultureNotExists(languageDa); + await umbracoApi.templates.ensureNameNotExists(rootDocTypeName); + }); + + test.afterEach(async ({page, umbracoApi}) => { + await umbracoApi.content.deleteAllContent(); + await umbracoApi.documentTypes.ensureNameNotExists(rootDocTypeName); + await umbracoApi.languages.ensureCultureNotExists(languageDa); + await umbracoApi.templates.ensureNameNotExists(rootDocTypeName); + }); + + async function createDocWithCultureVariationWithContent(umbracoApi, name, alias, language1, language2, value, isPublished){ + const rootDocType = new DocumentTypeBuilder() + .withName(name) + .withAlias(alias) + .withAllowAsRoot(true) + .withAllowCultureVariation(true) + .withDefaultTemplate(alias) + .addGroup() + .withName("Content") + .addTextBoxProperty() + .withLabel("Title") + .withAlias("title") + .done() + .done() + .build(); + + await umbracoApi.documentTypes.save(rootDocType).then(async (generatedRootDocType) => { + const childContentNode = new ContentBuilder() + .withContentTypeAlias(generatedRootDocType["alias"]) + .withAction("publishNew") + .addVariant() + .withCulture(language1) + .withName(language1) + .withSave(true) + .withPublish(isPublished) + .addProperty() + .withAlias("title") + .withValue(value) + .done() + .done() + .addVariant() + .withCulture(language2) + .withName(language2) + .withSave(true) + .withPublish(isPublished) + .done() + .build(); + + await umbracoApi.content.save(childContentNode); + }); + } + + test('No edit button for content when language changed', async ({page, umbracoApi, umbracoUi}) => { + const alias = AliasHelper.toAlias(rootDocTypeName); + + await umbracoApi.languages.createLanguage(languageDa, false, 1); + await createDocWithCultureVariationWithContent(umbracoApi, rootDocTypeName, alias, languageEn, languageDa, "", false); + + await umbracoUi.refreshContentTree(); + await page.locator('[data-element="tree-item-' + languageEn + '"]').click(); + await page.locator('.umb-variant-switcher__toggle').click(); + await page.locator('.umb-variant-switcher__name-wrapper', {hasText: "Danish"}).click(); + + // Assert + await expect(await page.locator('.umb-property-editor__lock-overlay')).not.toBeVisible(); + + // Cleaned + }); + + test('Updating value and publishing non-default language only without saving default should not update value', async ({page, umbracoApi, umbracoUi}) => { + const text = 'USA'; + const updatedText = 'DENMARK'; + const endpoint = '/'; + const alias = AliasHelper.toAlias(rootDocTypeName); + + await umbracoApi.languages.createLanguage(languageDa, false, 1); + await createDocWithCultureVariationWithContent(umbracoApi, rootDocTypeName, alias, languageEn, languageDa, text, true); + await umbracoApi.templates.edit(rootDocTypeName, `@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage + @{ + Layout = null; + } + @{ + if (Model.HasValue("title")){ + @(Model.Value("title")) + } + } ` + ); + const contentId = await umbracoApi.content.getContentId(languageEn); + const langId = await umbracoApi.languages.getLanguageId(languageDa); + await umbracoApi.domain.createDomain(endpoint, contentId, langId); + + await umbracoUi.refreshContentTree(); + await page.locator('[data-element="tree-item-' + languageEn + '"]').click(); + await page.locator('.umb-variant-switcher__toggle').click(); + await page.locator('.umb-variant-switcher__name-wrapper', {hasText: "Danish"}).hover(); + await page.locator('[role="button"]', {hasText: "Open in split view"}).click(); + + await page.locator('.umb-split-view', {hasText: languageEn}).locator('[name="textbox"]').fill(updatedText); + await page.locator('[label-key="buttons_morePublishingOptions"]').click(); + await page.locator('.umb-list-item', {hasText: "English"}).locator('.umb-form-check__check').click(); + await page.locator('[alias="overlaySubmit"]').click(); + + // Assert + await page.waitForTimeout(500); + await expect(await umbracoApi.content.verifyRenderedContent(endpoint, text, true)).toBeTruthy(); + + // Cleaned + }); +}); \ No newline at end of file diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/AllowEditInvariantFromNonDefault=False/appsettings.json b/tests/Umbraco.Tests.AcceptanceTest/tests/AllowEditInvariantFromNonDefault=False/appsettings.json new file mode 100644 index 0000000000..7c5ae11742 --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/AllowEditInvariantFromNonDefault=False/appsettings.json @@ -0,0 +1,9 @@ +{ + "Umbraco": { + "CMS": { + "Content": { + "AllowEditInvariantFromNonDefault": false + } + } + } +} \ No newline at end of file diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/content.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/content.spec.ts new file mode 100644 index 0000000000..ea95f45dc3 --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/content.spec.ts @@ -0,0 +1,750 @@ +import {AliasHelper, ApiHelpers, ConstantHelper, test} from '@umbraco/playwright-testhelpers'; +import {expect} from "@playwright/test"; +import { + ContentBuilder, + DocumentTypeBuilder, + PartialViewMacroBuilder, + MacroBuilder, + GridDataTypeBuilder +} from "@umbraco/json-models-builders"; + +test.describe('Content tests', () => { + + test.beforeEach(async ({page, umbracoApi}) => { + // TODO: REMOVE THIS WHEN SQLITE IS FIXED + // Wait so we don't bombard the API + await page.waitForTimeout(1000); + await umbracoApi.login(); + }); + + async function createSimpleMacro(name, umbracoApi: ApiHelpers){ + const insertMacro = new PartialViewMacroBuilder() + .withName(name) + .withContent(`@inherits Umbraco.Cms.Web.Common.Macros.PartialViewMacroPage +

Acceptance test

`) + .build(); + + const macroWithPartial = new MacroBuilder() + .withName(name) + .withPartialViewMacro(insertMacro) + .withRenderInEditor() + .withUseInEditor() + .build(); + + await umbracoApi.macros.saveWithPartial(macroWithPartial); + } + + test('Copy content', async ({ page, umbracoApi, umbracoUi }) => { + const rootDocTypeName = "Test document type"; + const childDocTypeName = "Child test document type"; + const firstRootNodeName = "1) Home"; + const childNodeName = "1) Child"; + const secondRootNodeName = "2) Home"; + + await umbracoApi.content.deleteAllContent(); + await umbracoApi.documentTypes.ensureNameNotExists(rootDocTypeName); + await umbracoApi.documentTypes.ensureNameNotExists(childDocTypeName); + + const childDocType = new DocumentTypeBuilder() + .withName(childDocTypeName) + .build() + + const createdChildDocType = await umbracoApi.documentTypes.save(childDocType); + + const rootDocType = new DocumentTypeBuilder() + .withName(rootDocTypeName) + .withAllowAsRoot(true) + .withAllowedContentTypes(createdChildDocType.id) + .build(); + + const createdRootDocType = await umbracoApi.documentTypes.save(rootDocType); + + // TODO: Make some constants for actions. + const rootContentNode = new ContentBuilder() + .withContentTypeAlias(createdRootDocType.alias) + .withAction("saveNew") + .addVariant() + .withName(firstRootNodeName) + .withSave(true) // We should probably just default to true... + .done() + .build(); + + const savedRootNode = await umbracoApi.content.save(rootContentNode); + + const secondRootNode = new ContentBuilder() + .withContentTypeAlias(createdRootDocType.alias) + .withAction("saveNew") + .addVariant() + .withName(secondRootNodeName) + .withSave(true) + .done() + .build(); + + await umbracoApi.content.save(secondRootNode); + + const childContentNode = new ContentBuilder() + .withContentTypeAlias(createdChildDocType.alias) + .withAction("saveNew") + .withParent(savedRootNode.id) + .addVariant() + .withName(childNodeName) + .withSave(true) + .done() + .build(); + + await umbracoApi.content.save(childContentNode); + + await umbracoUi.refreshContentTree(); + + await umbracoUi.clickElement(umbracoUi.getTreeItem("content", [firstRootNodeName, childNodeName]), {button: "right", force: true}) + await umbracoUi.clickElement(umbracoUi.getContextMenuAction(ConstantHelper.actions.copy)) + await page.locator('.umb-pane [data-element="tree-item-' + secondRootNodeName + '"]').click(); + await page.locator('.umb-dialog-footer > .btn-primary').click(); + await expect(page.locator('.alert-success')).toBeVisible(); + + await umbracoApi.documentTypes.ensureNameNotExists(rootDocTypeName); + await umbracoApi.documentTypes.ensureNameNotExists(childDocTypeName); + }); + + test('Move content', async ({ page, umbracoApi, umbracoUi }) => { + const rootDocTypeName = "Test document type"; + const childDocTypeName = "Child test document type"; + const firstRootNodeName = "1) Home"; + const childNodeName = "1) Child"; + const secondRootNodeName = "2) Home"; + + await umbracoApi.content.deleteAllContent(); + await umbracoApi.documentTypes.ensureNameNotExists(rootDocTypeName); + await umbracoApi.documentTypes.ensureNameNotExists(childDocTypeName); + + const childDocType = new DocumentTypeBuilder() + .withName(childDocTypeName) + .build() + + const createdChildDocType = await umbracoApi.documentTypes.save(childDocType); + + const rootDocType = new DocumentTypeBuilder() + .withName(rootDocTypeName) + .withAllowAsRoot(true) + .withAllowedContentTypes(createdChildDocType.id) + .build(); + + const createdRootDocType = await umbracoApi.documentTypes.save(rootDocType); + + const rootContentNode = new ContentBuilder() + .withContentTypeAlias(createdRootDocType.alias) + .withAction("saveNew") + .addVariant() + .withName(firstRootNodeName) + .withSave(true) // We should probably just default to true... + .done() + .build(); + + const savedRootNode = await umbracoApi.content.save(rootContentNode); + + const secondRootNode = new ContentBuilder() + .withContentTypeAlias(createdRootDocType.alias) + .withAction("saveNew") + .addVariant() + .withName(secondRootNodeName) + .withSave(true) + .done() + .build(); + + await umbracoApi.content.save(secondRootNode); + + const childContentNode = new ContentBuilder() + .withContentTypeAlias(createdChildDocType.alias) + .withAction("saveNew") + .withParent(savedRootNode.id) + .addVariant() + .withName(childNodeName) + .withSave(true) + .done() + .build(); + + await umbracoApi.content.save(childContentNode); + + await umbracoUi.refreshContentTree(); + + await umbracoUi.clickElement(umbracoUi.getTreeItem("content", [firstRootNodeName, childNodeName]), { button: "right", force: true }); + await umbracoUi.clickElement(umbracoUi.getContextMenuAction(ConstantHelper.actions.move)) + await page.locator('.umb-pane [data-element="tree-item-' + secondRootNodeName + '"]').click() + await page.locator('[key="actions_move"]').click(); + + await expect(page.locator('.alert-success')).toBeVisible(); + + await umbracoApi.documentTypes.ensureNameNotExists(rootDocTypeName); + await umbracoApi.documentTypes.ensureNameNotExists(childDocTypeName); + }); + + test('Sort content', async ({ page, umbracoApi, umbracoUi }) => { + const rootDocTypeName = "Test document type"; + const childDocTypeName = "Child test document type"; + const rootNodeName = "1) Home"; + const firstChildNodeName = "1) Child"; + const secondChildNodeName = "2) Child"; + + await umbracoApi.content.deleteAllContent(); + await umbracoApi.documentTypes.ensureNameNotExists(rootDocTypeName); + await umbracoApi.documentTypes.ensureNameNotExists(childDocTypeName); + + const childDocType = new DocumentTypeBuilder() + .withName(childDocTypeName) + .build(); + const createdChildDocType = await umbracoApi.documentTypes.save(childDocType); + + const rootDocType = new DocumentTypeBuilder() + .withName(rootDocTypeName) + .withAllowAsRoot(true) + .withAllowedContentTypes(createdChildDocType.id) + .build(); + const createdRootDocType = await umbracoApi.documentTypes.save(rootDocType); + + const rootContentNode = new ContentBuilder() + .withContentTypeAlias(createdRootDocType.alias) + .withAction("saveNew") + .addVariant() + .withName(rootNodeName) + .withSave(true) + .done() + .build(); + const createdRootContentNode = await umbracoApi.content.save(rootContentNode); + + // Add an item under root node + const firstChildContentNode = new ContentBuilder() + .withContentTypeAlias(createdChildDocType.alias) + .withAction("saveNew") + .withParent(createdRootContentNode.id) + .addVariant() + .withName(firstChildNodeName) + .withSave(true) + .done() + .build(); + await umbracoApi.content.save(firstChildContentNode); + + // Add a second item under root node + const secondChildContentNode = new ContentBuilder() + .withContentTypeAlias(createdChildDocType.alias) + .withAction("saveNew") + .withParent(createdRootContentNode.id) + .addVariant() + .withName(secondChildNodeName) + .withSave(true) + .done() + .build(); + await umbracoApi.content.save(secondChildContentNode); + + await umbracoUi.refreshContentTree(); + await umbracoUi.clickElement(umbracoUi.getTreeItem("content", [rootNodeName]), { button: "right", force: true }); + await umbracoUi.clickElement(umbracoUi.getContextMenuAction(ConstantHelper.actions.sort)); + // Drag'n'drop second child to be the first one. + await page.locator('.ui-sortable-handle >> text=' + secondChildNodeName).hover(); + await page.mouse.down() + await page.locator('.ui-sortable-handle >> text=' + firstChildNodeName).hover(); + await page.mouse.up(); + + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.save)); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.close)); + + const childNodes = await page.locator('[node="child"]'); + await expect(childNodes.first()).toContainText(secondChildNodeName); + await expect(childNodes.nth(2)).toContainText(firstChildNodeName); + + await umbracoApi.documentTypes.ensureNameNotExists(rootDocTypeName); + await umbracoApi.documentTypes.ensureNameNotExists(childDocTypeName); + }); + + test('Rollback content', async ({ page, umbracoApi, umbracoUi }) => { + const rootDocTypeName = "Test document type"; + const initialNodeName = "Home node"; + const newNodeName = "Home"; + + await umbracoApi.content.deleteAllContent(); + await umbracoApi.documentTypes.ensureNameNotExists(rootDocTypeName); + + const rootDocType = new DocumentTypeBuilder() + .withName(rootDocTypeName) + .withAllowAsRoot(true) + .build(); + const createdDocType = await umbracoApi.documentTypes.save(rootDocType); + + const rootContentNode = new ContentBuilder() + .withContentTypeAlias(createdDocType.alias) + .addVariant() + .withName(initialNodeName) + .withSave(true) + .done() + .build(); + await umbracoApi.content.save(rootContentNode); + + await umbracoUi.refreshContentTree(); + await umbracoUi.clickElement(umbracoUi.getTreeItem("content", [initialNodeName])); + + const header = await page.locator('#headerName') + // Sadly playwright doesn't have a clear method for inputs :( + // so we have to triple click to select all, and then hit backspace... + await header.click({ clickCount: 3 }) + await page.keyboard.press('Backspace'); + + await umbracoUi.setEditorHeaderName(newNodeName); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.saveAndPublish)); + await umbracoUi.isSuccessNotificationVisible(); + + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.rollback)); + // Not a very nice selector, but there's sadly no alternative :( + await page.locator('.-selectable.cursor-pointer').first().click(); + // Sadly can't use the button by label key here since there's another one in the DOM + await page.locator('[action="vm.rollback()"]').click(); + + await umbracoUi.refreshContentTree(); + await expect(page.locator('.umb-badge >> text=Save')).toBeVisible(); + await expect(page.locator('.umb-badge >> text=RollBack')).toBeVisible(); + const node = await umbracoUi.getTreeItem("content", [initialNodeName]) + await expect(node).toBeVisible(); + }); + + test('View audit trail', async ({ page, umbracoApi, umbracoUi }) => { + const rootDocTypeName = "Test document type"; + const nodeName = "Home"; + const labelName = "Name"; + + await umbracoApi.documentTypes.ensureNameNotExists(rootDocTypeName); + await umbracoApi.content.deleteAllContent(); + + const rootDocType = new DocumentTypeBuilder() + .withName(rootDocTypeName) + .withAllowAsRoot(true) + .addGroup() + .addTextBoxProperty() + .withLabel(labelName) + .done() + .done() + .build(); + + const generatedRootDocType = await umbracoApi.documentTypes.save(rootDocType) + + const rootContentNode = new ContentBuilder() + .withContentTypeAlias(generatedRootDocType["alias"]) + .addVariant() + .withName(nodeName) + .withSave(true) + .done() + .build(); + + await umbracoApi.content.save(rootContentNode); + + // Refresh to update the tree + await umbracoUi.refreshContentTree(); + + // Access node + await umbracoUi.clickElement(umbracoUi.getTreeItem('content', [nodeName])); + + // Navigate to Info app + await page.locator(ConstantHelper.contentApps.info).click(); + + // Assert + await expect(await page.locator('.history')).toBeDefined(); + + // Clean up (content is automatically deleted when document types are gone) + await umbracoApi.documentTypes.ensureNameNotExists(rootDocTypeName); + }); + + test('Save draft', async ({ page, umbracoApi, umbracoUi }) => { + const rootDocTypeName = "Test document type"; + const nodeName = "Home"; + const expected = "Unpublished"; + + await umbracoApi.content.deleteAllContent(); + await umbracoApi.documentTypes.ensureNameNotExists(rootDocTypeName); + + const rootDocType = new DocumentTypeBuilder() + .withName(rootDocTypeName) + .withAllowAsRoot(true) + .build(); + + const generatedRootDocType = await umbracoApi.documentTypes.save(rootDocType); + + const rootContentNode = new ContentBuilder() + .withContentTypeAlias(generatedRootDocType["alias"]) + .withAction("saveNew") + .addVariant() + .withName(nodeName) + .withSave(true) + .done() + .build(); + + await umbracoApi.content.save(rootContentNode) + + // Refresh to update the tree + await umbracoUi.refreshContentTree(); + + // Access node + await umbracoUi.clickElement(umbracoUi.getTreeItem('content', [nodeName])); + + // Assert + await expect(page.locator('[data-element="node-info-status"]').locator('.umb-badge')).toContainText(expected); + + // Clean up (content is automatically deleted when document types are gone) + await umbracoApi.documentTypes.ensureNameNotExists(rootDocTypeName); + }); + + test('Preview draft', async ({ page, umbracoApi, umbracoUi }) => { + const rootDocTypeName = "Test document type"; + const nodeName = "Home"; + + await umbracoApi.content.deleteAllContent(); + await umbracoApi.documentTypes.ensureNameNotExists(rootDocTypeName); + + const rootDocType = new DocumentTypeBuilder() + .withName(rootDocTypeName) + .withAllowAsRoot(true) + .build(); + + const generatedRootDocType = await umbracoApi.documentTypes.save(rootDocType); + + const rootContentNode = new ContentBuilder() + .withContentTypeAlias(generatedRootDocType["alias"]) + .withAction("saveNew") + .addVariant() + .withName(nodeName) + .withSave(true) + .done() + .build(); + + await umbracoApi.content.save(rootContentNode) + + // Refresh to update the tree + await umbracoUi.refreshContentTree(); + + // Access node + await umbracoUi.clickElement(umbracoUi.getTreeItem('content', [nodeName])); + + // Assert + await expect(page.locator('[alias="preview"]')).toBeVisible(); + await page.locator('[alias="preview"]').click(); + await umbracoUi.isSuccessNotificationVisible(); + + // Clean up (content is automatically deleted when document types are gone) + await umbracoApi.documentTypes.ensureNameNotExists(rootDocTypeName); + }); + + test('Publish draft', async ({ page, umbracoApi, umbracoUi }) => { + const rootDocTypeName = "Test document type"; + const nodeName = "Home"; + const expected = "Published"; + + await umbracoApi.content.deleteAllContent(); + await umbracoApi.documentTypes.ensureNameNotExists(rootDocTypeName); + + const rootDocType = new DocumentTypeBuilder() + .withName(rootDocTypeName) + .withAllowAsRoot(true) + .build(); + + const generatedRootDocType = await umbracoApi.documentTypes.save(rootDocType); + + const rootContentNode = new ContentBuilder() + .withContentTypeAlias(generatedRootDocType["alias"]) + .addVariant() + .withName(nodeName) + .withSave(true) + .done() + .build(); + + await umbracoApi.content.save(rootContentNode) + + // Refresh to update the tree + await umbracoUi.refreshContentTree(); + + // Access node + await umbracoUi.clickElement(umbracoUi.getTreeItem('content', [nodeName])); + + // Assert + await expect(page.locator('[data-element="node-info-status"]').locator('.umb-badge')).toContainText(expected); + + // Clean up (content is automatically deleted when document types are gone) + await umbracoApi.documentTypes.ensureNameNotExists(rootDocTypeName); + }); + + test('Content with contentpicker', async ({ page, umbracoApi, umbracoUi }) => { + const pickerDocTypeName = 'Content picker doc type'; + const pickerDocTypeAlias = AliasHelper.toAlias(pickerDocTypeName); + const pickedDocTypeName = 'Picked content document type'; + const pickedDocTypeAlias = AliasHelper.toAlias(pickedDocTypeName); + + await umbracoApi.content.deleteAllContent(); + await umbracoApi.documentTypes.ensureNameNotExists(pickerDocTypeName); + await umbracoApi.templates.ensureNameNotExists(pickerDocTypeName); + await umbracoApi.documentTypes.ensureNameNotExists(pickedDocTypeName); + + // Create the content type and content we'll be picking from. + const pickedDocType = new DocumentTypeBuilder() + .withName(pickedDocTypeName) + .withAlias(pickedDocTypeAlias) + .withAllowAsRoot(true) + .addGroup() + .addTextBoxProperty() + .withAlias('text') + .done() + .done() + .build(); + + const generatedType = await umbracoApi.documentTypes.save(pickedDocType) + const pickedContentNode = new ContentBuilder() + .withContentTypeAlias(generatedType["alias"]) + .withAction("publishNew") + .addVariant() + .withName('Content to pick') + .withSave(true) + .withPublish(true) + .addProperty() + .withAlias('text') + .withValue('Acceptance test') + .done() + .withSave(true) + .withPublish(true) + .done() + .build(); + await umbracoApi.content.save(pickedContentNode); + + // Create the doctype with a the picker + const pickerDocType = new DocumentTypeBuilder() + .withName(pickerDocTypeName) + .withAlias(pickerDocTypeAlias) + .withAllowAsRoot(true) + .withDefaultTemplate(pickerDocTypeAlias) + .addGroup() + .withName('ContentPickerGroup') + .addContentPickerProperty() + .withAlias('picker') + .done() + .done() + .build(); + + await umbracoApi.documentTypes.save(pickerDocType); + + // Ugly wait, but we have to wait for cache to rebuild + await page.waitForTimeout(1000); + // Edit it the template to allow us to verify the rendered view. + await umbracoApi.templates.edit(pickerDocTypeName, `@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage + @{ + Layout = null; + var pickedItem = Model.Picker as PickedContentDocumentType; + } + +

@pickedItem.Text

`); + + // Create content with content picker + await page.locator('.umb-tree-root-link').click({ button: "right"}); + await umbracoUi.clickElement(umbracoUi.getContextMenuAction(ConstantHelper.actions.create)); + await page.locator('[data-element="action-create-' + pickerDocTypeAlias + '"] > .umb-action-link').click(); + + // Fill out content + await umbracoUi.setEditorHeaderName('ContentPickerContent') + await page.locator('.umb-node-preview-add').click(); + + // Should really try and find a better way to do this, but umbracoTreeItem tries to click the content pane in the background + await page.locator('[ng-if="vm.treeReady"] > .umb-tree .umb-tree-item__inner').click(); + + // We have to wait for the picked content to show up or it wont be added. + await expect(await page.locator('.umb-node-preview__description')).toBeVisible(); + + // Save and publish + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.saveAndPublish)); + await umbracoUi.isSuccessNotificationVisible(); + + // Assert + const expectedContent = '

Acceptance test

' + await expect(await umbracoApi.content.verifyRenderedContent('/contentpickercontent', expectedContent, true)).toBeTruthy(); + + // Clean up + await umbracoApi.documentTypes.ensureNameNotExists(pickerDocTypeName); + await umbracoApi.templates.ensureNameNotExists(pickerDocTypeName); + await umbracoApi.documentTypes.ensureNameNotExists(pickedDocTypeName); + }); + + test('Content with macro in RTE', async ({ page, umbracoApi, umbracoUi }) => { + const viewMacroName = 'Content with macro in RTE'; + const partialFileName = viewMacroName + '.cshtml'; + + await umbracoApi.macros.ensureNameNotExists(viewMacroName); + await umbracoApi.partialViews.ensureMacroFileNameNotExists(partialFileName); + await umbracoApi.documentTypes.ensureNameNotExists(viewMacroName); + await umbracoApi.templates.ensureNameNotExists(viewMacroName); + await umbracoApi.content.deleteAllContent(); + + // First thing first we got to create the macro we will be inserting + await createSimpleMacro(viewMacroName, umbracoApi); + + // Now we need to create a document type with a rich text editor where we can insert the macro + // The document type must have a template as well in order to ensure that the content is displayed correctly + const alias = AliasHelper.toAlias(viewMacroName); + const docType = new DocumentTypeBuilder() + .withName(viewMacroName) + .withAlias(alias) + .withAllowAsRoot(true) + .withDefaultTemplate(alias) + .addGroup() + .addRichTextProperty() + .withAlias('text') + .done() + .done() + .build(); + + const generatedDocType = await umbracoApi.documentTypes.save(docType) + // Might as wel initally create the content here, the less GUI work during the test the better + const contentNode = new ContentBuilder() + .withContentTypeAlias(generatedDocType["alias"]) + .withAction('saveNew') + .addVariant() + .withName(viewMacroName) + .withSave(true) + .done() + .build(); + + await umbracoApi.content.save(contentNode); + + // Ugly wait but we have to wait for cache to rebuild + await page.waitForTimeout(1000); + + // Edit the macro template in order to have something to verify on when rendered. + await umbracoApi.templates.edit(viewMacroName, `@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage +@{ + Layout = null; +} +@{ + if (Model.HasValue("text")){ + @(Model.Value("text")) + } +} `); + + // Enter content + await umbracoUi.refreshContentTree(); + await umbracoUi.clickElement(umbracoUi.getTreeItem("content", [viewMacroName])); + + // Insert macro + await page.locator('#mceu_13-button').click(); + await page.locator('.umb-card-grid-item', {hasText: viewMacroName}).click(); + // cy.get('.umb-card-grid-item').contains(viewMacroName).click(); + + // Save and publish + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.saveAndPublish)); + await umbracoUi.isSuccessNotificationVisible(); + + // Ensure that the view gets rendered correctly + const expected = `

Acceptance test

 

`; + await expect(await umbracoApi.content.verifyRenderedContent('/', expected, true)).toBeTruthy(); + + // Cleanup + await umbracoApi.macros.ensureNameNotExists(viewMacroName); + await umbracoApi.partialViews.ensureMacroFileNameNotExists(partialFileName); + await umbracoApi.documentTypes.ensureNameNotExists(viewMacroName); + await umbracoApi.templates.ensureNameNotExists(viewMacroName); + }); + + test('Content with macro in grid', async ({ page, umbracoApi, umbracoUi }) => { + const name = 'Content with macro in grid'; + const macroName = 'Grid macro'; + const macroFileName = macroName + '.cshtml'; + + await umbracoApi.dataTypes.ensureNameNotExists(name); + await umbracoApi.documentTypes.ensureNameNotExists(name); + await umbracoApi.templates.ensureNameNotExists(name); + await umbracoApi.macros.ensureNameNotExists(macroName); + await umbracoApi.partialViews.ensureMacroFileNameNotExists(macroFileName); + await umbracoApi.content.deleteAllContent(); + + await createSimpleMacro(macroName, umbracoApi); + + const grid = new GridDataTypeBuilder() + .withName(name) + .withDefaultGrid() + .build(); + + const alias = AliasHelper.toAlias(name); + + // Save grid and get the ID + const dataType = await umbracoApi.dataTypes.save(grid) + + // Create a document type using the data type + const docType = new DocumentTypeBuilder() + .withName(name) + .withAlias(alias) + .withAllowAsRoot(true) + .withDefaultTemplate(alias) + .addGroup() + .addCustomProperty(dataType['id']) + .withAlias('grid') + .done() + .done() + .build(); + + const generatedDocType = await umbracoApi.documentTypes.save(docType); + const contentNode = new ContentBuilder() + .withContentTypeAlias(generatedDocType["alias"]) + .addVariant() + .withName(name) + .withSave(true) + .done() + .build(); + + await umbracoApi.content.save(contentNode); + + // Ugly wait but we have to wait for cache to rebuild + await page.waitForTimeout(1000); + + // Edit the template to allow us to verify the rendered view + await umbracoApi.templates.edit(name, `@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage + @{ + Layout = null; + } +@Html.GetGridHtml(Model, "grid")`); + + // Act + // Enter content + await umbracoUi.refreshContentTree(); + await umbracoUi.clickElement(umbracoUi.getTreeItem("content", [name])); + + // Click add + await page.locator(':nth-child(2) > .preview-row > .preview-col > .preview-cell').click(); // Choose 1 column layout. + await page.locator('.umb-column > .templates-preview > :nth-child(2) > small').click(); // Choose headline + await page.locator('.umb-cell-placeholder').click(); + // Click macro + await page.locator(':nth-child(4) > .umb-card-grid-item > :nth-child(1)').click(); + // Select the macro + await page.locator(`.umb-card-grid-item[title='${macroName}']`).click('bottom'); + + + // Save and publish + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.saveAndPublish)); + await umbracoUi.isSuccessNotificationVisible(); + + const expected = ` +
+
+
+
+
+
+
+

Acceptance test

+
+
+
+
+
+
+
` + + await expect(await umbracoApi.content.verifyRenderedContent('/', expected, true)).toBeTruthy(); + + // Clean + await umbracoApi.dataTypes.ensureNameNotExists(name); + await umbracoApi.documentTypes.ensureNameNotExists(name); + await umbracoApi.templates.ensureNameNotExists(name); + await umbracoApi.macros.ensureNameNotExists(macroName); + await umbracoApi.partialViews.ensureMacroFileNameNotExists(macroFileName); + }); +}); + diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/recycleBin.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/recycleBin.spec.ts new file mode 100644 index 0000000000..56d60bad2f --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/recycleBin.spec.ts @@ -0,0 +1,70 @@ +import {test} from '@umbraco/playwright-testhelpers'; +import {expect} from "@playwright/test"; +import {ContentBuilder, DocumentTypeBuilder} from "@umbraco/json-models-builders"; + +test.describe('Recycle bin', () => { + + test.beforeEach(async ({page, umbracoApi}) => { + // TODO: REMOVE THIS WHEN SQLITE IS FIXED + // Wait so we don't bombard the API + await page.waitForTimeout(1000); + await umbracoApi.login(); + }); + + test('Can delete content from recycle bin', async ({page, umbracoApi, umbracoUi}) => { + const contentToDeleteName = "DeleteMe"; + const contentToNotDeleteName = "DontDelete"; + const testType = "TestType"; + + await umbracoApi.documentTypes.ensureNameNotExists(testType); + + const docType = new DocumentTypeBuilder() + .withName(testType) + .build(); + + await umbracoApi.documentTypes.save(docType).then(async (savedDocType) => { + const contentToDelete = new ContentBuilder() + .withContentTypeAlias(savedDocType.alias) + .withAction("saveNew") + .addVariant() + .withName(contentToDeleteName) + .withSave(true) + .done() + .build(); + + const contentToNotDelete = new ContentBuilder() + .withContentTypeAlias(savedDocType.alias) + .withAction("saveNew") + .addVariant() + .withName(contentToNotDeleteName) + .withSave(true) + .done() + .build(); + + // Put it in the recycle bin + await umbracoApi.content.save(contentToDelete).then(async savedToDelete => { + await umbracoApi.content.deleteById(savedToDelete.id); + }); + await umbracoApi.content.save(contentToNotDelete).then(async savedNotToDelete => { + await umbracoApi.content.deleteById(savedNotToDelete.id) + }); + }); + + await umbracoUi.refreshContentTree(); + await umbracoUi.clickElement(umbracoUi.getTreeItem('content', ["Recycle Bin"])); + await page.locator('.umb-content-grid__content', {hasText: contentToDeleteName}).click(); + // cy.get('.umb-content-grid__content').contains(contentToDeleteName).closest('div').click(); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey("actions_delete")); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey('contentTypeEditor_yesDelete')); + + // Assert + await umbracoUi.isSuccessNotificationVisible(); + + await expect(page.locator('.umb-content-grid__content', {hasText: contentToDeleteName})).not.toBeVisible(); + await expect(await umbracoUi.getTreeItem('content', ["Recycle Bin", contentToDeleteName])).not.toBeVisible(); + await expect(page.locator('.umb-content-grid__content', {hasText: contentToNotDeleteName})).toBeVisible(); + + // Clean up + await umbracoApi.documentTypes.ensureNameNotExists(testType); + }); +}); diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/routing.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/routing.spec.ts new file mode 100644 index 0000000000..2290153e71 --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/routing.spec.ts @@ -0,0 +1,257 @@ +import {ApiHelpers, ConstantHelper, test} from '@umbraco/playwright-testhelpers'; +import {expect} from "@playwright/test"; +import {ContentBuilder, DocumentTypeBuilder} from "@umbraco/json-models-builders"; + +test.describe('Routing', () => { + let swedishLanguageId = 0; + const swedishCulture = "sv"; + const danishCulture = "da" + const nodeName = "Root"; + const childNodeName = "Child"; + const grandChildNodeName = "Grandchild"; + const rootDocTypeName = "Test document type"; + + test.beforeEach(async ({page, umbracoApi}) => { + // TODO: REMOVE THIS WHEN SQLITE IS FIXED + // Wait so we don't bombard the API + await page.waitForTimeout(1000); + await umbracoApi.login(); + await umbracoApi.content.deleteAllContent(); + await umbracoApi.documentTypes.ensureNameNotExists(rootDocTypeName); + await umbracoApi.languages.ensureCultureNotExists(danishCulture); + await umbracoApi.languages.ensureCultureNotExists(swedishCulture); + }); + + test.afterEach(async ({page, umbracoApi}) => { + await umbracoApi.login(); + await umbracoApi.content.deleteAllContent(); + await umbracoApi.documentTypes.ensureNameNotExists(rootDocTypeName); + await umbracoApi.languages.ensureCultureNotExists(danishCulture); + await umbracoApi.languages.ensureCultureNotExists(swedishCulture); + }); + + async function saveNewLanguages(umbracoApi: ApiHelpers) { + // Save Danish + const url = process.env.URL + "/umbraco/backoffice/umbracoapi/language/SaveLanguage"; + const danishRequestBody = { + culture: danishCulture + } + + await umbracoApi.post(url, danishRequestBody); + + // Save Swedish + const swedishRequestBody = { + culture: swedishCulture + } + + await umbracoApi.post(url, swedishRequestBody).then((response) => { + swedishLanguageId = response["id"]; + }); + } + + async function configureDomain(id, name, lang, umbracoApi: ApiHelpers) { + //Save domain for child node + const url = process.env.URL + "/umbraco/backoffice/umbracoapi/content/PostSaveLanguageAndDomains" + const body = { + nodeId: id, + domains: [ + { + name: name, + lang: lang + }], + language: 0 + } + + await umbracoApi.post(url, body); + } + + test('Root node published in language A, Child node published in language A', async ({page, umbracoApi, umbracoUi}) => { + const rootDocType = new DocumentTypeBuilder() + .withName(rootDocTypeName) + .withAllowAsRoot(true) + .withAllowCultureVariation(true) + .build(); + + await saveNewLanguages(umbracoApi); + + await umbracoApi.documentTypes.save(rootDocType).then(async (generatedRootDocType) => { + const rootContentNode = new ContentBuilder() + .withContentTypeAlias(generatedRootDocType["alias"]) + .withAction("publishNew") + .addVariant() + .withCulture('en-US') + .withName(nodeName) + .withSave(true) + .withPublish(true) + .done() + .build(); + + await umbracoApi.content.save(rootContentNode).then(async (generatedRootContent) => { + const childContentNode = new ContentBuilder() + .withContentTypeAlias(generatedRootDocType["alias"]) + .withAction("saveNew") + .withParent(generatedRootContent["id"]) + .addVariant() + .withCulture('en-US') + .withName(childNodeName) + .withSave(true) + .done() + .build(); + + await umbracoApi.content.save(childContentNode); + }); + }); + + // Refresh to update the tree + await umbracoUi.refreshContentTree(); + await umbracoUi.clickElement(umbracoUi.getTreeItem("content", [nodeName, childNodeName])); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.saveAndPublish)); + + // Pop-up with what cultures you want to publish shows, click it + await page.locator('.btn-success').last().click() + + // Assert + await umbracoUi.isSuccessNotificationVisible(); + }); + + test( 'Root node published in language A, Child node published in language B', async ({page, umbracoApi, umbracoUi}) => { + const rootDocType = new DocumentTypeBuilder() + .withName(rootDocTypeName) + .withAllowAsRoot(true) + .withAllowCultureVariation(true) + .build(); + + await saveNewLanguages(umbracoApi); + + await umbracoApi.documentTypes.save(rootDocType).then(async (generatedRootDocType) => { + const rootContentNode = new ContentBuilder() + .withContentTypeAlias(generatedRootDocType["alias"]) + .withAction("publishNew") + .addVariant() + .withCulture('en-US') + .withName(nodeName) + .withSave(true) + .withPublish(true) + .done() + .build(); + + await umbracoApi.content.save(rootContentNode).then(async (generatedRootContent) => { + const childContentNode = new ContentBuilder() + .withContentTypeAlias(generatedRootDocType["alias"]) + .withAction("saveNew") + .withParent(generatedRootContent["id"]) + .addVariant() + .withCulture('en-US') + .withName(childNodeName) + .withSave(true) + .done() + .addVariant() + .withCulture(swedishCulture) + .withName("Bärn") + .withSave(true) + .done() + .build(); + + await umbracoApi.content.save(childContentNode); + }); + }); + + // Refresh to update the tree + await umbracoUi.refreshContentTree(); + await umbracoUi.clickElement(umbracoUi.getTreeItem("content", [nodeName, childNodeName])); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.saveAndPublish)); + + await expect(await page.locator('.umb-list')).toBeVisible(); + await page.locator('.checkbox').last().click(); + // Pop-up with what cultures you want to publish shows, click it + await page.locator('.btn-success').last().click() + + // Assert + await expect(await umbracoUi.getSuccessNotification()).toHaveCount(2); + await expect(await page.locator('.alert-warning')).toBeVisible(); + }); + + test('Root node published in language A, Child node published in language A + B, Grandchild published in A + B', async ({page, umbracoApi, umbracoUi}) => { + const rootDocType = new DocumentTypeBuilder() + .withName(rootDocTypeName) + .withAllowAsRoot(true) + .withAllowCultureVariation(true) + .build(); + + await saveNewLanguages(umbracoApi); + + await umbracoApi.documentTypes.save(rootDocType).then(async (generatedRootDocType) => { + const rootContentNode = new ContentBuilder() + .withContentTypeAlias(generatedRootDocType["alias"]) + .withAction("publishNew") + .addVariant() + .withCulture('en-US') + .withName(nodeName) + .withSave(true) + .withPublish(true) + .done() + .build(); + + await umbracoApi.content.save(rootContentNode).then(async (generatedRootContent) => { + + await configureDomain(generatedRootContent["id"], "/en", 1, umbracoApi); + const childContentNode = new ContentBuilder() + .withContentTypeAlias(generatedRootDocType["alias"]) + .withAction("saveNew") + .withParent(generatedRootContent["id"]) + .addVariant() + .withCulture('en-US') + .withName(childNodeName) + .withSave(true) + .done() + .addVariant() + .withCulture(swedishCulture) + .withName("Barn") + .withSave(true) + .done() + .build(); + + await umbracoApi.content.save(childContentNode).then(async(generatedChildContent) => { + + await configureDomain(generatedChildContent["id"], "/sv", swedishLanguageId, umbracoApi); + const grandChildContentNode = new ContentBuilder() + .withContentTypeAlias(generatedRootDocType["alias"]) + .withAction("saveNew") + .withParent(generatedChildContent["id"]) + .addVariant() + .withCulture('en-US') + .withName(grandChildNodeName) + .withSave(true) + .done() + .addVariant() + .withCulture(swedishCulture) + .withName("Barnbarn") + .withSave(true) + .done() + .build(); + + await umbracoApi.content.save(grandChildContentNode); + }); + }); + }); + + // Refresh to update the tree + await umbracoUi.refreshContentTree(); + await umbracoUi.clickElement(umbracoUi.getTreeItem("content", [nodeName, childNodeName])); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.saveAndPublish)); + + await expect(await page.locator('.umb-list')).toBeVisible(); + await page.locator('.checkbox').last().click(); + await page.locator('.btn-success').last().click() + + await umbracoUi.clickMultiple(page.locator('.alert-success > .close')); + await umbracoUi.clickElement(umbracoUi.getTreeItem("content", [nodeName, childNodeName, grandChildNodeName])); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.saveAndPublish)); + + await expect(await page.locator('.umb-list')).toBeVisible(); + await page.locator('.checkbox').last().click(); + await page.locator('.btn-success').last().click() + // Assert + await expect(await umbracoUi.getSuccessNotification()).toHaveCount(2); + }) +}); diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/DataTypes/textBoxVariation.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/DataTypes/textBoxVariation.spec.ts new file mode 100644 index 0000000000..43af1f94be --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/DataTypes/textBoxVariation.spec.ts @@ -0,0 +1,292 @@ +import {expect} from "@playwright/test"; +import {AliasHelper, ConstantHelper, test} from '@umbraco/playwright-testhelpers'; +import {ContentBuilder, DocumentTypeBuilder, DomainBuilder} from "@umbraco/json-models-builders"; + +test.describe('Vary by culture for TextBox', () => { + + test.beforeEach(async ({page, umbracoApi, umbracoUi}) => { + // TODO: REMOVE THIS WHEN SQLITE IS FIXED + // Wait so we don't bombard the API + await page.waitForTimeout(1000); + await umbracoApi.login(); + }); + + test('create documentType with vary by culture with UI with a textbox property which also has vary by culture', async ({page, umbracoApi, umbracoUi}) => { + const documentTypeName = 'Test Document'; + const textBoxPropertyName = 'TestBox'; + const groupName = 'textBoxGroup'; + + await umbracoApi.documentTypes.ensureNameNotExists(documentTypeName); + await umbracoApi.templates.ensureNameNotExists(documentTypeName); + + await umbracoUi.goToSection(ConstantHelper.sections.settings); + + // Creates document with a template + await umbracoUi.createNewDocumentTypeWithTemplate(); + + // Updates permission for document + await umbracoUi.updateDocumentPermissionsToAllowCultureVariant(); + + await umbracoUi.setEditorHeaderName(documentTypeName); + + // Adds a group with a TextBox editor + await umbracoUi.goToAddEditor(groupName,textBoxPropertyName); + await page.locator('[data-element="datatype-Textbox"]').click(); + await page.locator('[data-element="datatypeconfig-Textstring"]').click(); + await page.locator('[data-element="button-submit"]').click(); + + // Saves the document + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.save)); + + // Assert + await expect(page.locator('.umb-notifications__notifications > .alert-success')).toBeVisible(); + + // Clean + await umbracoApi.documentTypes.ensureNameNotExists(documentTypeName); + await umbracoApi.templates.ensureNameNotExists(documentTypeName); + }); + + test('create content with two languages with different text', async ({page, umbracoApi, umbracoUi}) => { + const documentName = "Test Document"; + const languageEn = 'en-US'; + const languageDa = 'da'; + const enValue = "USA"; + const daValue = "DENMARK"; + const alias = AliasHelper.toAlias(documentName); + + await umbracoApi.content.deleteAllContent(); + await umbracoApi.documentTypes.ensureNameNotExists(documentName); + await umbracoApi.languages.ensureCultureNotExists(languageDa); + await umbracoApi.templates.ensureNameNotExists(documentName); + + await umbracoApi.languages.createLanguage(languageDa, false, 1); + const rootDocType = new DocumentTypeBuilder() + .withName(documentName) + .withAlias(alias) + .withAllowAsRoot(true) + .withAllowCultureVariation(true) + .withDefaultTemplate(alias) + .addGroup() + .withName("Content") + .addTextBoxProperty() + .withLabel("Title") + .withAlias("title") + .withCultureVariant(true) + .done() + .done() + .build(); + await umbracoApi.documentTypes.save(rootDocType); + + await umbracoUi.goToSection(ConstantHelper.sections.content); + + // Creates content item with the created document type + await umbracoUi.createContentWithDocumentType(documentName); + + // Adds title and input text for English culture + await page.locator('[data-element="editor-name-field"]').type(languageEn); + await page.locator('[name="textbox"]').type(enValue); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.save)); + await page.locator('[alias="overlaySubmit"]').click(); + + // Switches to Danish culture + await umbracoUi.switchCultureInContent("Danish") + + // Adds title and input text for Danish culture + await page.locator('[data-element="editor-name-field"]').type(languageDa); + await page.locator('[name="textbox"]').type(daValue); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.save)); + await page.locator('[alias="overlaySubmit"]').click(); + + // Assert + await page.locator('.umb-variant-switcher__toggle').click(); + await page.locator('.umb-variant-switcher__name-wrapper', {hasText: "English (United States)"}).hover(); + await page.locator('[role="button"]', {hasText: "Open in split view"}).click(); + await expect(page.locator('[name="textbox"]').first()).toHaveValue(daValue); + await expect(page.locator('[name="textbox"]').nth(1)).toHaveValue(enValue); + + // Clean + await umbracoApi.content.deleteAllContent(); + await umbracoApi.documentTypes.ensureNameNotExists(documentName); + await umbracoApi.languages.ensureCultureNotExists(languageDa); + await umbracoApi.templates.ensureNameNotExists(documentName); + }); + + test('publish content with two languages with different text', async ({page, umbracoApi, umbracoUi}) => { + const documentName = "Test Document"; + const languageEn = 'en-US'; + const languageDa = 'da'; + const enValue = "USA"; + const daValue = "DENMARK"; + const alias = AliasHelper.toAlias(documentName); + + await umbracoApi.content.deleteAllContent(); + await umbracoApi.documentTypes.ensureNameNotExists(documentName); + await umbracoApi.languages.ensureCultureNotExists(languageDa); + await umbracoApi.templates.ensureNameNotExists(documentName); + + await umbracoApi.languages.createLanguage(languageDa, false, 1); + const rootDocType = new DocumentTypeBuilder() + .withName(documentName) + .withAlias(alias) + .withAllowAsRoot(true) + .withAllowCultureVariation(true) + .withDefaultTemplate(alias) + .addGroup() + .withName("Content") + .addTextBoxProperty() + .withLabel("Title") + .withAlias("title") + .withCultureVariant(true) + .done() + .done() + .build(); + + await umbracoApi.documentTypes.save(rootDocType).then(async (generatedRootDocType) => { + const childContentNode = new ContentBuilder() + .withContentTypeAlias(generatedRootDocType["alias"]) + .withAction("publishNew") + .addVariant() + .withCulture(languageEn) + .withName(languageEn) + .withSave(true) + .withPublish(false) + .addProperty() + .withAlias("title") + .withValue(enValue) + .done() + .done() + .addVariant() + .withCulture(languageDa) + .withName(languageDa) + .withSave(true) + .withPublish(false) + .addProperty() + .withAlias("title") + .withValue(daValue) + .done() + .done() + .build(); + await umbracoApi.content.save(childContentNode); + }); + await umbracoUi.refreshContentTree(); + + // Opens content + await page.locator('[data-element="tree-item-' + languageEn + '"]').click(); + + // Publishes both languages + await page.locator('[label-key="buttons_morePublishingOptions"]').click(); + await page.locator('.umb-list-item', {hasText: "Danish"}).locator('.umb-form-check__check').click(); + await page.locator('[alias="overlaySubmit"]').click(); + + // Assert + await expect(page.locator('.umb-notifications__notifications > .alert-success', {hasText: "English"})).toBeVisible(); + await expect(page.locator('.umb-notifications__notifications > .alert-success', {hasText: "Danish"})).toBeVisible(); + + // Clean + await umbracoApi.content.deleteAllContent(); + await umbracoApi.documentTypes.ensureNameNotExists(documentName); + await umbracoApi.languages.ensureCultureNotExists(languageDa); + await umbracoApi.templates.ensureNameNotExists(documentName); + }); + + test('Check if content for two languages is published on domains with their own values', async ({page, umbracoApi, umbracoUi}) => { + const documentName = "Test Document"; + const languageEn = 'en-US'; + const languageDa = 'da'; + const enValue = "USA"; + const daValue = "DENMARK"; + const alias = AliasHelper.toAlias(documentName); + const daEndpoint = "/"; + const enEndpoint = "/en"; + + await umbracoApi.content.deleteAllContent(); + await umbracoApi.documentTypes.ensureNameNotExists(documentName); + await umbracoApi.languages.ensureCultureNotExists(languageDa); + await umbracoApi.templates.ensureNameNotExists(documentName); + + await umbracoApi.languages.createLanguage(languageDa, false, 1); + const rootDocType = new DocumentTypeBuilder() + .withName(documentName) + .withAlias(alias) + .withAllowAsRoot(true) + .withAllowCultureVariation(true) + .withDefaultTemplate(alias) + .addGroup() + .withName("Content") + .addTextBoxProperty() + .withLabel("Title") + .withAlias("title") + .withCultureVariant(true) + .done() + .done() + .build(); + + await umbracoApi.documentTypes.save(rootDocType).then(async (generatedRootDocType) => { + const childContentNode = new ContentBuilder() + .withContentTypeAlias(generatedRootDocType["alias"]) + .withAction("publishNew") + .addVariant() + .withCulture(languageEn) + .withName(languageEn) + .withSave(true) + .withPublish(true) + .addProperty() + .withAlias("title") + .withValue(enValue) + .done() + .done() + .addVariant() + .withCulture(languageDa) + .withName(languageDa) + .withSave(true) + .withPublish(true) + .addProperty() + .withAlias("title") + .withValue(daValue) + .done() + .done() + .build(); + await umbracoApi.content.save(childContentNode); + }); + + await umbracoApi.templates.edit(documentName, `@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage + @{ + Layout = null; + } + @{ + if (Model.HasValue("title")){ + @(Model.Value("title")) + } + } ` + ); + + // Gets id of content and the languages for creating domains. + const contentId = await umbracoApi.content.getContentId(languageEn); + const langIdDa = await umbracoApi.languages.getLanguageId(languageDa); + const langIdEn = await umbracoApi.languages.getLanguageId(languageEn); + const domains = new DomainBuilder() + .withNodeId(contentId) + .addDomain() + .withEndpoint(daEndpoint) + .withLanguageId(langIdDa) + .done() + .addDomain() + .withEndpoint(enEndpoint) + .withLanguageId(langIdEn) + .done() + .build() + await umbracoApi.domain.save(domains); + + // Assert + await page.waitForTimeout(500); + await expect(await umbracoApi.content.verifyRenderedContent(daEndpoint, daValue, true)).toBeTruthy(); + await expect(await umbracoApi.content.verifyRenderedContent(enEndpoint, enValue, true)).toBeTruthy(); + + // Clean + await umbracoApi.content.deleteAllContent(); + await umbracoApi.documentTypes.ensureNameNotExists(documentName); + await umbracoApi.languages.ensureCultureNotExists(languageDa); + await umbracoApi.templates.ensureNameNotExists(documentName); + }); +}); + diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/HelpPanel/systemInformation.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/HelpPanel/systemInformation.spec.ts new file mode 100644 index 0000000000..1707e38fbf --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/HelpPanel/systemInformation.spec.ts @@ -0,0 +1,52 @@ +import {ConstantHelper, test, UiHelpers} from '@umbraco/playwright-testhelpers'; +import {expect, Page} from "@playwright/test"; + +test.describe('System Information', () => { + const enCulture = "en-US"; + const dkCulture = "da-DK"; + + test.beforeEach(async ({page, umbracoApi}) => { + // TODO: REMOVE THIS WHEN SQLITE IS FIXED + // Wait so we don't bombard the API + await page.waitForTimeout(1000); + await umbracoApi.login(); + await umbracoApi.users.setCurrentLanguage(enCulture); + }); + + test.afterEach(async ({page, umbracoApi}) => { + await umbracoApi.users.setCurrentLanguage(enCulture); + }); + + async function openSystemInformation(page: Page, umbracoUi : UiHelpers) { + //We have to wait for page to load, if the site is slow + await umbracoUi.clickElement(umbracoUi.getGlobalHelp()); + await expect(page.locator('.umb-help-list-item').last()).toBeVisible(); + await umbracoUi.clickElement(page.locator('.umb-help-list-item').last()); + await page.locator('.umb-drawer-content').scrollIntoViewIfNeeded(); + } + + test('Check System Info Displays', async ({page, umbracoApi, umbracoUi}) => { + await openSystemInformation(page, umbracoUi); + await expect(page.locator('.table').locator('tr')).toHaveCount(14); + await expect(await page.locator("tr", {hasText: "Current Culture"})).toContainText(enCulture); + await expect(await page.locator("tr", {hasText: "Current UI Culture"})).toContainText(enCulture); + }); + + test('Checks language displays correctly after switching', async ({page, umbracoApi, umbracoUi}) => { + //Navigate to edit user and change language + await umbracoUi.clickElement(umbracoUi.getGlobalUser()); + await page.locator('[alias="editUser"]').click(); + await page.locator('[name="culture"]').selectOption({value: "string:da-DK"}); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.save), {force: true}); + await umbracoUi.isSuccessNotificationVisible(); + + await openSystemInformation(page, umbracoUi); + + //Assert + await expect(await page.locator("tr", {hasText: "Current Culture"})).toContainText(dkCulture); + await expect(await page.locator("tr", {hasText: "Current UI Culture"})).toContainText(dkCulture); + + // Close the help panel + await page.locator('.umb-button__content').last().click(); + }); + }); diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Languages/languages.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Languages/languages.spec.ts new file mode 100644 index 0000000000..31781d3002 --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Languages/languages.spec.ts @@ -0,0 +1,65 @@ +import {ConstantHelper, test} from '@umbraco/playwright-testhelpers'; +import {expect} from "@playwright/test"; + +test.describe('Languages', () => { + + test.beforeEach(async ({page, umbracoApi}) => { + // TODO: REMOVE THIS WHEN SQLITE IS FIXED + // Wait so we don't bombard the API + await page.waitForTimeout(1000); + await umbracoApi.login(); + }); + + test('Creates a language', async ({page, umbracoApi, umbracoUi}) => { + const language = 'Danish'; + const culture = 'da'; + + await umbracoApi.languages.ensureCultureNotExists(culture); + + await umbracoUi.goToSection(ConstantHelper.sections.settings); + await umbracoUi.waitForTreeLoad(ConstantHelper.sections.settings); + + await umbracoUi.clickElement(umbracoUi.getTreeItem('settings', ['languages'])); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey('languages_addLanguage')); + await page.locator('select[name="newLang"]').selectOption({label: language}); + + // Save and assert success + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.save)); + await umbracoUi.isSuccessNotificationVisible(); + + // Cleanup + await umbracoApi.languages.ensureCultureNotExists(culture); + }); + + test('Deletes a language', async ({page, umbracoApi, umbracoUi}) => { + // Setup + const language1 = 'da'; + const language2 = 'en-GB'; + await umbracoApi.languages.ensureCultureNotExists(language1); + await umbracoApi.languages.ensureCultureNotExists(language2); + + await umbracoApi.languages.createLanguage(language1, true, 1); + await umbracoApi.languages.createLanguage(language2, true, 1); + + // Enter settings section and wait for everything to load + await umbracoUi.goToSection(ConstantHelper.sections.settings); + await umbracoUi.waitForTreeLoad(ConstantHelper.sections.settings); + + // Enter language tree and select the language we just created + await umbracoUi.clickElement(umbracoUi.getTreeItem('settings', ['languages'])); + + // Assert there are 3 languages + await expect(await page.locator('tbody > tr')).toHaveCount(3); + + // Delete UK Language + await page.locator('umb-button[label-key="general_delete"]').last().click(); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey('contentTypeEditor_yesDelete')); + + // Assert there is only 2 languages + await expect(await page.locator('tbody > tr')).toHaveCount(2); + + // Cleanup + await umbracoApi.languages.ensureCultureNotExists(language1); + await umbracoApi.languages.ensureCultureNotExists(language2); + }); +}); diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Login/login.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Login/login.spec.ts new file mode 100644 index 0000000000..5a008c32c5 --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Login/login.spec.ts @@ -0,0 +1,70 @@ +import { test, expect } from '@playwright/test'; +test.describe('Login', () => { + + test.beforeEach(async ({ page }) => { + // TODO: REMOVE THIS WHEN SQLITE IS FIXED + // Wait so we don't bombard the API + await page.waitForTimeout(1000); + await page.goto(process.env.URL + '/umbraco'); + }); + test('Login with correct username and password', async ({page}) => { + + let error = page.locator('.text-error'); + await expect(error).toBeHidden(); + + // Action + await page.fill('#umb-username', process.env.UMBRACO_USER_LOGIN); + await page.fill('#umb-passwordTwo', process.env.UMBRACO_USER_PASSWORD); + await page.locator('[label-key="general_login"]').click(); + await page.waitForNavigation(); + + // Assert + await expect(page).toHaveURL(process.env.URL + '/umbraco#/content'); + let usernameField = await page.locator('#umb-username'); + let passwordField = await page.locator('#umb-passwordTwo'); + await expect(usernameField).toHaveCount(0); + await expect(passwordField).toHaveCount(0); + }); + + test('Login with correct username but wrong password', async({page}) => { + const username = process.env.UMBRACO_USER_LOGIN; + const password = 'wrong'; + + // Precondition + let error = page.locator('.text-error'); + await expect(error).toBeHidden(); + + // Action + await page.fill('#umb-username', username); + await page.fill('#umb-passwordTwo', password); + await page.locator('[label-key="general_login"]').click(); + + // Assert + let usernameField = await page.locator('#umb-username'); + let passwordField = await page.locator('#umb-passwordTwo'); + await expect(error).toBeVisible(); + await expect(usernameField).toBeVisible(); + await expect(passwordField).toBeVisible(); + }); + + test('Login with wrong username and wrong password', async({page}) => { + const username = 'wrong-username'; + const password = 'wrong'; + + // Precondition + let error = page.locator('.text-error'); + await expect(error).toBeHidden(); + + // Action + await page.fill('#umb-username', username); + await page.fill('#umb-passwordTwo', password); + await page.locator('[label-key="general_login"]').click(); + + // Assert + let usernameField = await page.locator('#umb-username'); + let passwordField = await page.locator('#umb-passwordTwo'); + await expect(error).toBeVisible(); + await expect(usernameField).toBeVisible(); + await expect(passwordField).toBeVisible(); + }); +}); \ No newline at end of file diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Media/mediaFiles.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Media/mediaFiles.spec.ts new file mode 100644 index 0000000000..c0e14eeba6 --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Media/mediaFiles.spec.ts @@ -0,0 +1,414 @@ +import {expect} from "@playwright/test"; +import {ConstantHelper, test} from '@umbraco/playwright-testhelpers'; + +test.describe('media File Types', () => { + + test.beforeEach(async ({page, umbracoApi, umbracoUi}) => { + // TODO: REMOVE THIS WHEN SQLITE IS FIXED + // Wait so we don't bombard the API + await page.waitForTimeout(1000); + await umbracoApi.login(); + await umbracoUi.goToSection(ConstantHelper.sections.media); + await umbracoApi.media.deleteAllMedia(); + }); + + test.describe('create each File Types', () => { + test('create Article', async ({page, umbracoApi, umbracoUi}) => { + const articleName = "Article"; + const fileName = "Article.pdf"; + const path = fileName; + const mimeType = "application/pdf"; + await umbracoApi.media.ensureNameNotExists(articleName); + + // Action + await umbracoApi.media.createArticleWithFile(articleName, fileName, path, mimeType); + await umbracoUi.refreshMediaTree(); + + // Assert + await expect(page.locator(".umb-tree-item__inner", {hasText: articleName})).toBeVisible(); + + // Clean + await umbracoApi.media.ensureNameNotExists(articleName); + }); + + test('create Audio', async ({page, umbracoApi, umbracoUi}) => { + const audioName = "Audio"; + const fileName = "Audio.mp3"; + const path = fileName; + const mimeType = "audio/mp3" + await umbracoApi.media.ensureNameNotExists(audioName); + + // Action + await umbracoApi.media.createAudioWithFile(audioName, fileName, path, mimeType); + await umbracoUi.refreshMediaTree(); + + // Assert + await expect(page.locator(".umb-tree-item__inner", {hasText: audioName})).toBeVisible(); + + // Clean + await umbracoApi.media.ensureNameNotExists(audioName); + }); + + test('create File', async ({page, umbracoApi, umbracoUi}) => { + const fileItemName = "File"; + const fileName = "File.txt"; + const path = fileName; + const mimeType = "*/*"; + await umbracoApi.media.ensureNameNotExists(fileItemName); + + // Action + await umbracoApi.media.createFileWithFile(fileItemName, fileName, path, mimeType); + await umbracoUi.refreshMediaTree(); + + // Assert + await expect(page.locator(".umb-tree-item__inner", {hasText: fileItemName})).toBeVisible(); + + // Clean + await umbracoApi.media.ensureNameNotExists(fileItemName); + }); + + test('create Folder', async ({page, umbracoApi, umbracoUi}) => { + const folderName = "Folder"; + await umbracoApi.media.ensureNameNotExists(folderName); + + // Action + await umbracoApi.media.createDefaultFolder(folderName); + await umbracoUi.refreshMediaTree(); + + // Assert + await expect(page.locator(".umb-tree-item__inner", {hasText: folderName})).toBeVisible(); + + // Clean + await umbracoApi.media.ensureNameNotExists(folderName); + }); + + test('create Image', async ({page, umbracoApi, umbracoUi}) => { + const imageName = "Umbraco"; + const umbracoFileValue = {"src": "Umbraco.png"}; + const fileName = "Umbraco.png" + const path = fileName; + const mimeType = "image/png"; + await umbracoApi.media.ensureNameNotExists(imageName); + + // Action + await umbracoApi.media.createImageWithFile(imageName, umbracoFileValue, fileName, path, mimeType); + await umbracoUi.refreshMediaTree(); + + // Assert + await expect(page.locator(".umb-tree-item__inner", {hasText: imageName})).toBeVisible(); + + // Clean + await umbracoApi.media.ensureNameNotExists(imageName); + }); + + test('create VectorGraphics(SVG)', async ({page, umbracoApi, umbracoUi}) => { + const vectorGraphicsName = 'VectorGraphics'; + const fileName = "VectorGraphics.svg"; + const path = fileName; + const mimeType = "image/svg+xml"; + await umbracoApi.media.ensureNameNotExists(vectorGraphicsName); + + // Action + await umbracoApi.media.createVectorGraphicsWithFile(vectorGraphicsName, fileName, path, mimeType); + await umbracoUi.refreshMediaTree(); + + // Assert + await expect(page.locator(".umb-tree-item__inner", {hasText: vectorGraphicsName})).toBeVisible(); + + // Clean + await umbracoApi.media.ensureNameNotExists(vectorGraphicsName); + }); + + test('create Video', async ({page, umbracoApi, umbracoUi}) => { + const videoName = "Video"; + const fileName = "Video.mp4"; + const path = fileName; + const mimeType = "video/mp4"; + await umbracoApi.media.ensureNameNotExists(videoName); + + // Action + await umbracoApi.media.createVideoWithFile(videoName, fileName, path, mimeType); + await umbracoUi.refreshMediaTree(); + + // Assert + await expect(page.locator(".umb-tree-item__inner", {hasText: videoName})).toBeVisible(); + + // Clean + await umbracoApi.media.ensureNameNotExists(videoName); + }); + }); + + test.describe('create each File Types in a Folder', () => { + test('create Article in a Folder', async ({page, umbracoApi, umbracoUi}) => { + const parentName = 'ParentArticleFolder'; + const childName = 'ChildArticle'; + await umbracoApi.media.ensureNameNotExists(parentName); + await umbracoApi.media.ensureNameNotExists(childName); + const articlePath = "./fixtures/mediaLibrary/Article.pdf" + + // Action + await umbracoApi.media.createDefaultFolder(parentName); + await umbracoUi.refreshMediaTree(); + await umbracoUi.clickElement(umbracoUi.getTreeItem('media', [parentName]), { + button: "right", + force: true + }); + await page.locator('[data-element="action-create"]').click(); + await page.locator('[data-element="action-umbracoMediaArticle"]').click(); + await page.locator('[data-element="editor-name-field"]').type(childName); + await umbracoUi.fileUploader(articlePath); + + // Assert + await expect(page.locator('.alert-success')).toBeVisible(); + + // Clean + await umbracoApi.media.ensureNameNotExists(parentName); + await umbracoApi.media.ensureNameNotExists(childName); + }); + test('create Audio in a Folder', async ({page, umbracoApi, umbracoUi}) => { + const parentName = 'ParentAudioFolder'; + const childName = 'ChildAudio'; + await umbracoApi.media.ensureNameNotExists(parentName); + await umbracoApi.media.ensureNameNotExists(childName); + const childPath = "./fixtures/mediaLibrary/Audio.mp3" + + // Action + await umbracoApi.media.createDefaultFolder(parentName); + await umbracoUi.refreshMediaTree(); + await umbracoUi.clickElement(umbracoUi.getTreeItem('media', [parentName]), { + button: "right", + force: true + }); + await page.locator('[data-element="action-create"]').click(); + await page.locator('[data-element="action-umbracoMediaAudio"]').click(); + await page.locator('[data-element="editor-name-field"]').type(childName); + await umbracoUi.fileUploader(childPath); + + // Assert + await expect(page.locator('.alert-success')).toBeVisible(); + + // Clean + await umbracoApi.media.ensureNameNotExists(parentName); + await umbracoApi.media.ensureNameNotExists(childName); + }); + + test('create File in a Folder', async ({page, umbracoApi, umbracoUi}) => { + const parentName = 'ParentFileFolder'; + const childName = 'ChildFile'; + await umbracoApi.media.ensureNameNotExists(parentName); + await umbracoApi.media.ensureNameNotExists(childName); + const childPath = "./fixtures/mediaLibrary/File.txt" + + // Action + await umbracoApi.media.createDefaultFolder(parentName); + await umbracoUi.refreshMediaTree(); + await umbracoUi.clickElement(umbracoUi.getTreeItem('media', [parentName]), { + button: "right", + force: true + }); + await page.locator('[data-element="action-create"]').click(); + await page.locator('[data-element="action-File"]').click(); + await page.locator('[data-element="editor-name-field"]').type(childName); + await umbracoUi.fileUploader(childPath); + + // Assert + await expect(page.locator('.alert-success')).toBeVisible(); + + // Clean + await umbracoApi.media.ensureNameNotExists(parentName); + await umbracoApi.media.ensureNameNotExists(childName); + }); + + test('create Folder in a Folder', async ({page, umbracoApi, umbracoUi}) => { + const parentName = 'ParentFolderFolder'; + const childName = 'ChildFolder'; + await umbracoApi.media.ensureNameNotExists(parentName); + await umbracoApi.media.ensureNameNotExists(childName); + + // Action + await umbracoApi.media.createDefaultFolder(parentName); + await umbracoUi.refreshMediaTree(); + + await umbracoUi.clickElement(umbracoUi.getTreeItem('media', [parentName]), { + button: "right", + force: true + }); + await page.locator('[data-element="action-create"]').click(); + await page.locator('[data-element="action-Folder"]').click(); + await page.locator('[data-element="editor-name-field"]').type(childName); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.save)); + + // Assert + await expect(page.locator('.alert-success')).toBeVisible(); + + // Clean + await umbracoApi.media.ensureNameNotExists(parentName); + await umbracoApi.media.ensureNameNotExists(childName); + }); + + test('create Image in a Folder', async ({page, umbracoApi, umbracoUi}) => { + const parentName = 'ParentImageFolder'; + const childName = 'ChildImage'; + await umbracoApi.media.ensureNameNotExists(parentName); + await umbracoApi.media.ensureNameNotExists(childName); + const childPath = "./fixtures/mediaLibrary/Umbraco.png" + + // Action + await umbracoApi.media.createDefaultFolder(parentName); + await umbracoUi.refreshMediaTree(); + await umbracoUi.clickElement(umbracoUi.getTreeItem('media', [parentName]), { + button: "right", + force: true + }); + await page.locator('[data-element="action-create"]').click(); + await page.locator('[data-element="action-Image"]').click(); + await page.locator('[data-element="editor-name-field"]').type(childName); + await umbracoUi.fileUploader(childPath); + + // Assert + await expect(page.locator('.alert-success')).toBeVisible(); + + // Clean + await umbracoApi.media.ensureNameNotExists(parentName); + await umbracoApi.media.ensureNameNotExists(childName); + }); + + test('create VectorGraphics(SVG) in a Folder', async ({page, umbracoApi, umbracoUi}) => { + const parentName = 'ParentVectorGraphicsFolder'; + const childName = 'ChildVectorGraphics'; + await umbracoApi.media.ensureNameNotExists(parentName); + await umbracoApi.media.ensureNameNotExists(childName); + const childPath = "./fixtures/mediaLibrary/VectorGraphics.svg" + + // Action + await umbracoApi.media.createDefaultFolder(parentName); + await umbracoUi.refreshMediaTree(); + await umbracoUi.clickElement(umbracoUi.getTreeItem('media', [parentName]), { + button: "right", + force: true + }); + await page.locator('[data-element="action-create"]').click(); + await page.locator('[data-element="action-umbracoMediaVectorGraphics"]').click(); + await page.locator('[data-element="editor-name-field"]').type(childName); + await umbracoUi.fileUploader(childPath); + + // Assert + await expect(page.locator('.alert-success')).toBeVisible(); + + // Clean + await umbracoApi.media.ensureNameNotExists(parentName); + await umbracoApi.media.ensureNameNotExists(childName); + }); + + test('create Video in a Folder', async ({page, umbracoApi, umbracoUi}) => { + const parentName = 'ParentVideoFolder'; + const childName = 'ChildVideo'; + await umbracoApi.media.ensureNameNotExists(parentName); + await umbracoApi.media.ensureNameNotExists(childName); + const childPath = "./fixtures/mediaLibrary/Video.mp4" + + // Action + await umbracoApi.media.createDefaultFolder(parentName); + await umbracoUi.refreshMediaTree(); + await umbracoUi.clickElement(umbracoUi.getTreeItem('media', [parentName]), { + button: "right", + force: true + }); + await page.locator('[data-element="action-create"]').click(); + await page.locator('[data-element="action-umbracoMediaVideo"]').click(); + await page.locator('[data-element="editor-name-field"]').type(childName); + await umbracoUi.fileUploader(childPath); + + // Assert + await expect(page.locator('.alert-success')).toBeVisible(); + + // Clean + await umbracoApi.media.ensureNameNotExists(parentName); + await umbracoApi.media.ensureNameNotExists(childName); + }); + }); + + test('Delete one of each Files in media', async ({page, umbracoApi, umbracoUi}) => { + const articleName = 'ArticleToDelete'; + const audioName = 'AudioToDelete'; + const fileName = 'FileToDelete'; + const folderName = 'FolderToDelete'; + const imageName = 'ImageToDelete'; + const vectorGraphicsName = 'VectorGraphicsToDelete'; + const videoName = 'VideoToDelete'; + await umbracoApi.media.deleteAllFiles(articleName, audioName, fileName, folderName, imageName, vectorGraphicsName, videoName); + + // Action + await umbracoApi.media.createAllFileTypes(articleName, audioName, fileName, folderName, imageName, vectorGraphicsName, videoName); + await page.reload(); + // Needs to close tours when page has reloaded + await page.click('.umb-tour-step__close'); + + // Takes all the child elements in folder-grid. + await page.locator(".umb-folder-grid").locator("xpath=/*", {hasText: folderName}).click({ + position: { + x: 5, + y: 0 + } + }); + const files = await page.locator('[data-element="media-grid"]').locator("xpath=/*"); + await umbracoUi.clickMultiple(files); + await page.locator('[label-key="actions_delete"]').click(); + await page.locator('[alias="overlaySubmit"]').click(); + + // Assert + await expect(page.locator('.alert-success')).toBeVisible(); + await expect(page.locator(".umb-folder-grid")).toBeHidden(); + await expect(page.locator('[data-element="media-grid"]')).toBeHidden(); + + // Clean + await umbracoApi.media.clearRecycleBin(); + }); + + test('Update existing File with new name', async ({page, umbracoApi, umbracoUi}) => { + const fileItemNameOld = "File"; + const fileItemNameNew = "UpdatedFile"; + const fileName = "File.txt"; + const path = fileName; + const mimeType = "*/*"; + await umbracoApi.media.ensureNameNotExists(fileItemNameOld); + + // Action + await umbracoApi.media.createFileWithFile(fileItemNameOld, fileName, path, mimeType); + await umbracoUi.refreshMediaTree(); + await page.locator('[data-element="tree-item-' + fileItemNameOld + '"]').click(); + await page.locator('[data-element="editor-name-field"]').fill(fileItemNameNew); + await page.locator('[label-key="buttons_save"]').click(); + await umbracoUi.refreshMediaTree(); + + // Assert + await expect(page.locator(".umb-tree-item__inner", {hasText: fileItemNameNew})).toBeVisible(); + + // Clean + await umbracoApi.media.ensureNameNotExists(fileItemNameNew); + }); + + test('Update existing File with new File', async ({page, umbracoApi, umbracoUi}) => { + const fileItemName = "File"; + const fileName = "File.txt"; + const path = fileName; + const fileNameNew = "UpdatedFile.txt" + const pathNew = "./fixtures/mediaLibrary/" + fileNameNew; + const mimeType = "*/*"; + await umbracoApi.media.ensureNameNotExists(fileItemName); + + // Action + await umbracoApi.media.createFileWithFile(fileItemName, fileName, path, mimeType); + await umbracoUi.refreshMediaTree(); + await page.locator('[data-element="tree-item-' + fileItemName + '"]').click(); + await page.locator('[key="content_uploadClear"]').click(); + await umbracoUi.fileUploader(pathNew); + await page.locator('[label-key="buttons_save"]').click(); + + // Assert + await expect(page.locator('.alert-success')).toBeVisible(); + + // Clean + await umbracoApi.media.ensureNameNotExists(fileItemName); + }); +}); diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Media/mediaSection.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Media/mediaSection.spec.ts new file mode 100644 index 0000000000..424b1310fb --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Media/mediaSection.spec.ts @@ -0,0 +1,159 @@ +import {expect, request} from "@playwright/test"; +import {ConstantHelper, test} from '@umbraco/playwright-testhelpers'; + +test.describe('Media', () => { + + test.beforeEach(async ({page, umbracoApi, umbracoUi}) => { + // TODO: REMOVE THIS WHEN SQLITE IS FIXED + // Wait so we don't bombard the API + await page.waitForTimeout(1000); + await umbracoApi.login(); + await umbracoUi.goToSection(ConstantHelper.sections.media); + await umbracoApi.media.deleteAllMedia() + }); + + test('move one of each Files into a Folder', async ({page, umbracoApi, umbracoUi}) => { + const articleName = 'ArticleToMove'; + const audioName = 'AudioToMove'; + const fileName = 'FileToMove'; + const folderName = 'FolderToMove'; + const imageName = 'ImageToMove'; + const vectorGraphicsName = 'VectorGraphicsToMove'; + const videoName = 'VideoToMove'; + const folderToMoveTooName = 'MoveHere'; + + const mediaFileTypes = [ + {fileTypeNames: articleName}, + {fileTypeNames: audioName}, + {fileTypeNames: fileName}, + {fileTypeNames: imageName}, + {fileTypeNames: vectorGraphicsName}, + {fileTypeNames: videoName} + ] + + await umbracoApi.media.deleteAllFiles(articleName,audioName,fileName,folderName,imageName,vectorGraphicsName,videoName); + await umbracoApi.media.ensureNameNotExists(folderToMoveTooName); + + // Action + await umbracoApi.media.createAllFileTypes(articleName, audioName, fileName, folderName, imageName, vectorGraphicsName, videoName); + await umbracoApi.media.createDefaultFolder(folderToMoveTooName); + await page.reload(); + // Needs to close tours when page has reloaded + await page.click('.umb-tour-step__close'); + const files = await page.locator('[data-element="media-grid"]').locator("xpath=/*"); + await umbracoUi.clickMultiple(files); + // I set an position on the click event so it does not click on the folder name and open it. + await page.locator(".umb-folder-grid").locator("xpath=/*", {hasText: folderName}).click({ + position: { + x: 5, + y: 0 + } + }); + await page.locator('[label-key="actions_move"]').click(); + await page.locator('[data-element="editor-container"] >> "' + folderToMoveTooName + '"').click(); + await page.locator('[label-key="general_submit"]').click(); + + // Assert + await page.waitForTimeout(500); + await umbracoUi.refreshMediaTree(); + await page.locator('[data-element="tree-item-' + folderToMoveTooName + '"]').click(); + for (const names of mediaFileTypes) { + await expect(page.locator('[data-element="media-grid"]', {hasText: names.fileTypeNames})).toBeVisible(); + } + await expect(page.locator(".umb-folder-grid", {hasText: folderName})).toBeVisible(); + + // Clean + await umbracoApi.media.deleteAllFiles(articleName, audioName, fileName, folderName, imageName, vectorGraphicsName, videoName); + await umbracoApi.media.ensureNameNotExists(folderToMoveTooName); + }); + + test('sort by Name', async ({page, umbracoApi, umbracoUi}) => { + const FolderNameA = 'A'; + const FolderNameB = 'B'; + const FolderNameC = 'C'; + await umbracoApi.media.ensureNameNotExists(FolderNameA); + await umbracoApi.media.ensureNameNotExists(FolderNameB); + await umbracoApi.media.ensureNameNotExists(FolderNameC); + + // Action + await umbracoApi.media.createDefaultFolder(FolderNameC); + await umbracoApi.media.createDefaultFolder(FolderNameB); + await umbracoApi.media.createDefaultFolder(FolderNameA); + + await umbracoUi.refreshMediaTree(); + await page.locator('[element="tree-item-options"]', {hasText: "Media"}).click({button: "right", force: true}); + await page.locator('[data-element="action-sort"]').click(); + await page.locator('.table-sortable >> [key="general_name"]').click(); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.save)); + + // Assert + const item = await page.locator('[ui-sortable="vm.sortableOptions"]').locator("xpath=/*[1]") + await expect(item).toContainText(FolderNameA); + + // Clean + await umbracoApi.media.ensureNameNotExists(FolderNameA); + await umbracoApi.media.ensureNameNotExists(FolderNameB); + await umbracoApi.media.ensureNameNotExists(FolderNameC); + }); + + test('search after a specific Folder', async ({page, umbracoApi, umbracoUi}) => { + const FolderSearchName = 'SearchMe'; + await umbracoApi.media.ensureNameNotExists(FolderSearchName); + + // Action + await umbracoApi.media.createDefaultFolder(FolderSearchName) + await page.locator('[model="options.filter"]').click(); + await page.locator('[placeholder="Type to search..."]').type(FolderSearchName); + + // Assert + await expect(page.locator(".umb-folder-grid__folder-description", {hasText: FolderSearchName})).toBeVisible(); + + // Clean + await umbracoApi.media.ensureNameNotExists(FolderSearchName); + }); + + test('change Grid to List', async ({page, umbracoApi, umbracoUi}) => { + const FolderOneName = 'FolderOne'; + const FolderTwoName = 'FolderTwo'; + await umbracoApi.media.ensureNameNotExists(FolderOneName); + await umbracoApi.media.ensureNameNotExists(FolderTwoName); + + // Action + await umbracoApi.media.createDefaultFolder(FolderOneName); + await umbracoApi.media.createDefaultFolder(FolderTwoName); + await umbracoUi.refreshMediaTree(); + await page.locator('[ng-click="vm.toggleLayoutDropdown()"]').click({force: true}); + await page.locator('[title="List"]').click(); + + // Assert + await expect(page.locator('[icon="icon-list"]')).toBeVisible(); + + // Clean + await umbracoApi.media.ensureNameNotExists(FolderOneName); + await umbracoApi.media.ensureNameNotExists(FolderTwoName); + }); + + test('change List to Grid', async ({page, umbracoApi, umbracoUi}) => { + const FolderOneName = 'FolderOne'; + const FolderTwoName = 'FolderTwo'; + await umbracoApi.media.ensureNameNotExists(FolderOneName); + await umbracoApi.media.ensureNameNotExists(FolderTwoName); + + // Action + await umbracoApi.media.createDefaultFolder(FolderOneName); + await umbracoApi.media.createDefaultFolder(FolderTwoName); + await umbracoUi.refreshMediaTree(); + await page.locator('[ng-click="vm.toggleLayoutDropdown()"]').click({force: true}); + await page.locator('[title="List"]').click(); + await umbracoUi.refreshMediaTree(); + await page.locator('[ng-click="vm.toggleLayoutDropdown()"]').click({force: true}); + await page.locator('[title="Grid"]').click(); + + // Assert + await expect(page.locator('[icon="icon-thumbnails-small"]')).toBeVisible(); + + // Clean + await umbracoApi.media.ensureNameNotExists(FolderOneName); + await umbracoApi.media.ensureNameNotExists(FolderTwoName); + }); +}); \ No newline at end of file diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Members/memberGroups.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Members/memberGroups.spec.ts new file mode 100644 index 0000000000..cdf8848404 --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Members/memberGroups.spec.ts @@ -0,0 +1,28 @@ +import {ConstantHelper, test} from '@umbraco/playwright-testhelpers'; + +test.describe('Packages', () => { + + test.beforeEach(async ({page, umbracoApi}) => { + // TODO: REMOVE THIS WHEN SQLITE IS FIXED + // Wait so we don't bombard the API + await page.waitForTimeout(1000); + await umbracoApi.login(); + }); + + test('Create member group', async ({page, umbracoApi, umbracoUi}) => { + const name = "Test Group"; + await umbracoApi.memberGroups.ensureNameNotExists(name); + await umbracoUi.goToSection(ConstantHelper.sections.member); + + await umbracoUi.clickElement(umbracoUi.getTreeItem("member", ["Member Groups"]), { button: "right"}); + await umbracoUi.clickElement(umbracoUi.getContextMenuAction(ConstantHelper.actions.create)); + await umbracoUi.setEditorHeaderName(name) + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.save)); + + // Assert + await umbracoUi.isSuccessNotificationVisible(); + + // Clean up + await umbracoApi.memberGroups.ensureNameNotExists(name); + }); +}); \ No newline at end of file diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Members/members.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Members/members.spec.ts new file mode 100644 index 0000000000..e27f05ac06 --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Members/members.spec.ts @@ -0,0 +1,38 @@ +import {ConstantHelper, test} from '@umbraco/playwright-testhelpers'; + +test.describe('Packages', () => { + + test.beforeEach(async ({page, umbracoApi}) => { + // TODO: REMOVE THIS WHEN SQLITE IS FIXED + // Wait so we don't bombard the API + await page.waitForTimeout(1000); + await umbracoApi.login(); + }); + + test('Create member', async ({page, umbracoApi, umbracoUi}) => { + const name = "Alice Bobson"; + const email = "alice-bobson@acceptancetest.umbraco"; + const password = "$AUlkoF*St0kgPiyyVEk5iU5JWdN*F7&"; + const passwordTimeout = 20000; + await umbracoApi.members.ensureEmailNotExists(email); + await umbracoUi.goToSection(ConstantHelper.sections.member); + await umbracoUi.clickElement(umbracoUi.getTreeItem("member", ["Members"]), { button: "right"}); + await umbracoUi.clickElement(umbracoUi.getContextMenuAction(ConstantHelper.actions.create)); + await umbracoUi.clickElement(page.locator('.menu-label').first()); + + await umbracoUi.setEditorHeaderName(name); + await page.locator('[data-element="sub-view-umbMembership"]').click(); + + await page.locator('input#_umb_login').type(email); + await page.locator('input#_umb_email').type(email); + await page.locator('input#password').type(password, { timeout: passwordTimeout }); + await page.locator('input#confirmPassword').type(password, { timeout: passwordTimeout }); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.save)); + + // Assert + await umbracoUi.isSuccessNotificationVisible(); + + // Clean up + await umbracoApi.members.ensureEmailNotExists(email); + }); +}); diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Packages/packages.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Packages/packages.spec.ts new file mode 100644 index 0000000000..9677418ae2 --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Packages/packages.spec.ts @@ -0,0 +1,100 @@ +import {expect} from '@playwright/test'; +import {ConstantHelper, test} from '@umbraco/playwright-testhelpers'; +import {ContentBuilder, DocumentTypeBuilder, PackageBuilder} from "@umbraco/json-models-builders"; + +test.describe('Packages', () => { + + const packageName = "TestPackage"; + const rootDocTypeName = "Test document type"; + const nodeName = "1) Home"; + + test.beforeEach(async ({page, umbracoApi}) => { + // TODO: REMOVE THIS WHEN SQLITE IS FIXED + // Wait so we don't bombard the API + await page.waitForTimeout(1000); + await umbracoApi.login(); + }); + + async function CreatePackage(umbracoApi, contentId) { + const packageSave = new PackageBuilder() + .withId(0) + .withPackageGuid("00000000-0000-0000-0000-000000000000") + .withContentNodeId(contentId) + .withName(packageName) + .build(); + + await umbracoApi.packages.save(packageSave); + } + + async function CreateSimplePackage(umbracoApi) { + const rootDocType = new DocumentTypeBuilder() + .withName(rootDocTypeName) + .withAllowAsRoot(true) + .build(); + + const generatedRootDocType = await umbracoApi.documentTypes.save(rootDocType); + + const rootDocTypeAlias = generatedRootDocType["alias"]; + + const rootContentNode = new ContentBuilder() + .withContentTypeAlias(rootDocTypeAlias) + .withAction("saveNew") + .addVariant() + .withName(nodeName) + .withSave(true) + .done() + .build(); + const generatedContent = await umbracoApi.content.save(rootContentNode); + await CreatePackage(umbracoApi, generatedContent.Id); + + } + + test('Deletes a package', async ({page, umbracoApi, umbracoUi}) => { + await umbracoApi.content.deleteAllContent(); + await umbracoApi.documentTypes.ensureNameNotExists(rootDocTypeName); + await umbracoApi.packages.ensureNameNotExists(packageName); + + await CreateSimplePackage(umbracoApi); + + // Navigate to create package section + await umbracoUi.goToSection(ConstantHelper.sections.packages); + await page.locator('[data-element="sub-view-umbCreatedPackages"]').click() + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.delete)); + await page.waitForTimeout(100); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey('contentTypeEditor_yesDelete')); + + // Assert + await expect(await page.locator("tr", {hasText: packageName})).not.toBeVisible(); + + // Cleanup + await umbracoApi.content.deleteAllContent(); + await umbracoApi.documentTypes.ensureNameNotExists(rootDocTypeName); + await umbracoApi.packages.ensureNameNotExists(packageName); + }); + + test('Download package', async ({page, umbracoApi, umbracoUi}) => { + await umbracoApi.content.deleteAllContent(); + await umbracoApi.documentTypes.ensureNameNotExists(rootDocTypeName); + await umbracoApi.packages.ensureNameNotExists(packageName); + + await CreateSimplePackage(umbracoApi); + + // Navigate to package and download + await umbracoUi.goToSection(ConstantHelper.sections.packages); + await page.locator('[data-element="sub-view-umbCreatedPackages"]').click(); + await page.locator("tr", {hasText: "TestPackage"}).click(); + const [download] = await Promise.all([ + page.waitForEvent('download'), + umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.download)) + ]); + + // Assert + await expect(await download).not.toBeNull(); + await expect(await download.failure()).toBeNull(); + + // Cleanup + await umbracoApi.content.deleteAllContent(); + await umbracoApi.documentTypes.ensureNameNotExists(rootDocTypeName); + await umbracoApi.packages.ensureNameNotExists(packageName); + }); +}); diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Settings/dataType.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Settings/dataType.spec.ts new file mode 100644 index 0000000000..a4ee573bdc --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Settings/dataType.spec.ts @@ -0,0 +1,66 @@ +import {ConstantHelper, test} from '@umbraco/playwright-testhelpers'; +import {expect} from "@playwright/test"; +import {LabelDataTypeBuilder} from "@umbraco/json-models-builders"; + +test.describe('Data Types', () => { + + test.beforeEach(async ({page, umbracoApi}) => { + // TODO: REMOVE THIS WHEN SQLITE IS FIXED + // Wait so we don't bombard the API + await page.waitForTimeout(1000); + await umbracoApi.login(); + }); + + test('Create data type', async ({ page, umbracoApi, umbracoUi }) => { + const name = "Test data type"; + + await umbracoApi.dataTypes.ensureNameNotExists(name); + + await umbracoUi.goToSection(ConstantHelper.sections.settings); + await umbracoUi.waitForTreeLoad(ConstantHelper.sections.settings); + + await umbracoUi.clickElement(umbracoUi.getTreeItem("settings", ["Data Types"]), { button: "right" }); + await umbracoUi.clickElement(umbracoUi.getContextMenuAction(ConstantHelper.actions.create)); + await umbracoUi.clickElement(umbracoUi.getContextMenuAction(ConstantHelper.actions.dataType)); + + await umbracoUi.setEditorHeaderName(name); + + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey('propertyEditorPicker_openPropertyEditorPicker')) + await page.locator('[title="Date/Time"]').click(); + + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.save)); + + // Assert + await umbracoUi.isSuccessNotificationVisible(); + + // Clean up + await umbracoApi.dataTypes.ensureNameNotExists(name); + }); + + test('Delete data type', async ({page, umbracoApi, umbracoUi}) => { + const name = "Test datatype"; + + await umbracoApi.dataTypes.ensureNameNotExists(name); + + const dataType = new LabelDataTypeBuilder() + .withSaveNewAction() + .withName(name) + .build(); + + await umbracoApi.dataTypes.save(dataType); + + await umbracoUi.goToSection(ConstantHelper.sections.settings); + await umbracoUi.waitForTreeLoad(ConstantHelper.sections.settings); + + await umbracoUi.clickElement(umbracoUi.getTreeItem("settings", ["Data Types", name]), { button: "right" }); + await umbracoUi.clickElement(umbracoUi.getContextMenuAction(ConstantHelper.actions.delete)); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.delete)); + + // Assert + await expect(await (page.locator(`text=${name}`))).toHaveCount(0); + + // Clean up + await umbracoApi.dataTypes.ensureNameNotExists(name); + }); +}); + diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Settings/documentTypes.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Settings/documentTypes.spec.ts new file mode 100644 index 0000000000..b33a3b5ccd --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Settings/documentTypes.spec.ts @@ -0,0 +1,86 @@ +import { expect } from '@playwright/test'; +import {ConstantHelper, test} from '@umbraco/playwright-testhelpers'; +import {DocumentTypeBuilder} from "@umbraco/json-models-builders"; + +test.describe('Document types', () => { + test.beforeEach(async ({ page, umbracoApi }) => { + // TODO: REMOVE THIS WHEN SQLITE IS FIXED + // Wait so we don't bombard the API + await page.waitForTimeout(1000); + await umbracoApi.login(); + }); + + test('Create document type', async ({ page, umbracoApi, umbracoUi }) => { + const name = "Test document type"; + + await umbracoApi.documentTypes.ensureNameNotExists(name); + await umbracoApi.templates.ensureNameNotExists(name); + + await umbracoUi.goToSection(ConstantHelper.sections.settings); + await umbracoUi.waitForTreeLoad(ConstantHelper.sections.settings); + await umbracoUi.clickElement(umbracoUi.getTreeItem("settings", ["Document Types"]), {button: "right"}) + + await umbracoUi.clickElement(umbracoUi.getContextMenuAction(ConstantHelper.actions.create)); + await umbracoUi.clickElement(umbracoUi.getContextMenuAction(ConstantHelper.actions.documentType)); + + await umbracoUi.setEditorHeaderName(name); + + // TODO: Create an GetButtonByDataElement? seems like it could be useful + // Add a property group + await page.locator('[data-element="group-add"]').click(); + await page.locator('.umb-group-builder__group-title-input').type('Group name'); + // Add a property + await page.locator('[data-element="property-add"]').click(); + await page.locator('.editor-label').type('property name'); + await page.locator('[data-element="editor-add"]').click(); + + // Search for textstring + await page.locator('#datatype-search').type('Textstring'); + + await page + .locator('ul.umb-card-grid li [title="Textstring"]') + .locator("xpath=ancestor::li") + .click(); + + await page.locator(".btn-success").last().click(); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.save)) + + // Assert + await umbracoUi.isSuccessNotificationVisible(); + + // Clean up + await umbracoApi.documentTypes.ensureNameNotExists(name); + await umbracoApi.templates.ensureNameNotExists(name); + }); + + test('Delete document type', async ({ page, umbracoApi, umbracoUi }) => { + const name = "Test document type"; + + await umbracoApi.documentTypes.ensureNameNotExists(name); + await umbracoApi.templates.ensureNameNotExists(name); + + const documentType = new DocumentTypeBuilder() + .withName(name) + .build(); + + await umbracoApi.documentTypes.save(documentType); + + await umbracoUi.goToSection(ConstantHelper.sections.settings); + await umbracoUi.waitForTreeLoad(ConstantHelper.sections.settings); + await umbracoUi.clickElement(umbracoUi.getTreeItem("settings", ["Document Types", name]), {button: "right"}) + await umbracoUi.clickElement(umbracoUi.getContextMenuAction(ConstantHelper.actions.delete)); + await page.locator('label.checkbox').click(); + + // This delete button for some reason does not have the usual general_delete label + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey("delete")); + + const docTypeLocator = await page.locator("text=" + name); + + // Assert + await expect(docTypeLocator).toHaveCount(0); + + // Clean up + await umbracoApi.documentTypes.ensureNameNotExists(name); + await umbracoApi.templates.ensureNameNotExists(name); + }); +}); diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Settings/languages.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Settings/languages.spec.ts new file mode 100644 index 0000000000..34b1f95278 --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Settings/languages.spec.ts @@ -0,0 +1,67 @@ +import {test, ApiHelpers, UiHelpers, ConstantHelper} from '@umbraco/playwright-testhelpers'; +import {expect} from "@playwright/test"; + +test.describe('Languages', () => { + test.beforeEach(async ({ page, umbracoApi }) => { + // TODO: REMOVE THIS WHEN SQLITE IS FIXED + // Wait so we don't bombard the API + await page.waitForTimeout(1000); + await umbracoApi.login(); + }); + + test('Can add language', async ({ page, umbracoApi, umbracoUi }) => { + // For some reason the languages to chose from seems to be translated differently than normal, as an example: + // My system is set to EN (US), but most languages are translated into Danish for some reason + // Aghem seems untranslated though? + const name = "Aghem"; // Must be an option in the select box + + await umbracoApi.languages.ensureNameNotExists(name); + + await umbracoUi.goToSection(ConstantHelper.sections.settings); + await umbracoUi.waitForTreeLoad(ConstantHelper.sections.settings); + + await umbracoUi.clickElement(umbracoUi.getTreeItem("settings", ["Languages"])); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey("languages_addLanguage")); + + await page.locator('select[name="newLang"]').selectOption({label: name}); + await page.locator('.btn-success').click(); + + await umbracoUi.isSuccessNotificationVisible(); + + await umbracoApi.languages.ensureNameNotExists(name); + }); + + test('Deletes language', async ({ page, umbracoApi, umbracoUi }) => { + + // Setup + const language1 = 'da'; + const language2 = 'en-GB'; + await umbracoApi.languages.ensureCultureNotExists(language1); + await umbracoApi.languages.ensureCultureNotExists(language2); + + await umbracoApi.languages.createLanguage(language1, true, 1); + await umbracoApi.languages.createLanguage(language2, true, 1); + + //Enter settings section and wait for everything to load + await umbracoUi.goToSection(ConstantHelper.sections.settings); + await umbracoUi.waitForTreeLoad(ConstantHelper.sections.settings); + + // Enter language tree and select the language we just created + await umbracoUi.clickElement(umbracoUi.getTreeItem('settings', ['Languages'])); + + // Assert there are 3 languages + let languages = page.locator('tbody > tr'); + await expect(languages).toHaveCount(3); + + // Delete UK Language + await page.locator('umb-button[label-key="general_delete"]').last().click(); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey('contentTypeEditor_yesDelete')); + + // Assert there is only 2 languages + await expect(page.locator('tbody > tr')).toHaveCount(2); + + // Cleanup + await umbracoApi.languages.ensureCultureNotExists(language1); + await umbracoApi.languages.ensureCultureNotExists(language2); + }); +}); diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Settings/mediaTypes.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Settings/mediaTypes.ts new file mode 100644 index 0000000000..8a071dacde --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Settings/mediaTypes.ts @@ -0,0 +1,54 @@ +import {test, ApiHelpers, UiHelpers, ConstantHelper} from '@umbraco/playwright-testhelpers'; +import {expect} from "@playwright/test"; + +test.describe('Media types', () => { + + test.beforeEach(async ({ page, umbracoApi }) => { + // TODO: REMOVE THIS WHEN SQLITE IS FIXED + // Wait so we don't bombard the API + await page.waitForTimeout(1000); + await umbracoApi.login(); + }); + + test('Create media type', async ({ page, umbracoApi, umbracoUi }) => { + + const name = "Test media type"; + await umbracoApi.mediaTypes.ensureNameNotExists(name); + + // Navigate to creation + await umbracoUi.goToSection(ConstantHelper.sections.settings); + await umbracoUi.waitForTreeLoad(ConstantHelper.sections.settings); + await umbracoUi.clickElement(umbracoUi.getTreeItem("settings", ["Media Types"]), {button: "right"}) + await umbracoUi.clickElement(umbracoUi.getContextMenuAction(ConstantHelper.actions.create)); + await page.locator('.menu-label localize[key="content_mediatype"]').click(); + + + // Fill out data + await umbracoUi.getEditorHeaderName(name); + + await page.locator('[data-element="group-add"]').click(); + + await page.locator('.umb-group-builder__group-title-input').fill('Group name'); + await page.locator('[data-element="property-add"]').click(); + await page.locator('.editor-label').fill('property name'); + await page.locator('[data-element="editor-add"]').click(); + + // Search for textstring + await page.locator('#datatype-search').fill('Textstring'); + + // Choose first item + await page.locator('ul.umb-card-grid [title="Textstring"]').first().click(); + + // Save property + await page.locator('.btn-success').last().click(); + + // Save + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.save)) + + // Assert + await umbracoUi.isSuccessNotificationVisible(); + + // Clean up + await umbracoApi.mediaTypes.ensureNameNotExists(name); + }); +}); \ No newline at end of file diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Settings/memberTypes.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Settings/memberTypes.spec.ts new file mode 100644 index 0000000000..464308388d --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Settings/memberTypes.spec.ts @@ -0,0 +1,51 @@ +import {ConstantHelper, test} from '@umbraco/playwright-testhelpers'; + +test.describe('Member Types', () => { + test.beforeEach(async ({page, umbracoApi}) => { + // TODO: REMOVE THIS WHEN SQLITE IS FIXED + // Wait so we don't bombard the API + await page.waitForTimeout(1000); + await umbracoApi.login(); + }); + + test('Create member type', async ({page, umbracoApi, umbracoUi}) => { + const name = "Test member type"; + + await umbracoApi.memberTypes.ensureNameNotExists(name); + + await umbracoUi.goToSection(ConstantHelper.sections.settings); + await umbracoUi.waitForTreeLoad(ConstantHelper.sections.settings); + + await umbracoUi.clickElement(umbracoUi.getTreeItem("settings", ["Member Types"]), { button: "right" }); + + await umbracoUi.clickElement(umbracoUi.getContextMenuAction("action-create")); + + //Type name + await umbracoUi.getEditorHeaderName(name); + + await page.locator('[data-element="group-add"]').click(); + + await page.locator('.umb-group-builder__group-title-input').type('Group name'); + await page.locator('[data-element="property-add"]').click(); + await page.locator('.editor-label').type('property name'); + await page.locator('[data-element="editor-add"]').click(); + + //Search for textstring + await page.locator('#datatype-search').type('Textstring'); + + // Choose first item + await page.locator('ul.umb-card-grid [title="Textstring"]').click(); + + // Save property + await page.locator('.btn-success').last().click(); + + //Save + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.save)); + + //Assert + await umbracoUi.isSuccessNotificationVisible(); + + //Clean up + await umbracoApi.memberTypes.ensureNameNotExists(name); + }); +}); diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Settings/partialViewMacroFiles.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Settings/partialViewMacroFiles.spec.ts new file mode 100644 index 0000000000..20636f3677 --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Settings/partialViewMacroFiles.spec.ts @@ -0,0 +1,154 @@ +import {expect} from '@playwright/test'; +import {ConstantHelper, test} from '@umbraco/playwright-testhelpers'; +import {PartialViewMacroBuilder} from "@umbraco/json-models-builders"; + +test.describe('Partial View Macro Files', () => { + + test.beforeEach(async ({page, umbracoApi}) => { + // TODO: REMOVE THIS WHEN SQLITE IS FIXED + // Wait so we don't bombard the API + await page.waitForTimeout(1000); + await umbracoApi.login(); + }); + + async function openPartialViewMacroCreatePanel(page, umbracoUi) { + await umbracoUi.goToSection('settings'); + await umbracoUi.waitForTreeLoad('settings'); + await umbracoUi.clickElement(umbracoUi.getTreeItem("settings", ["Partial View Macro Files"]), {button: "right"}); + await umbracoUi.clickElement(umbracoUi.getContextMenuAction("action-create")); + } + + async function cleanup(umbracoApi, name, extension = ".cshtml") { + const fileName = name + extension; + await umbracoApi.macros.ensureNameNotExists(name); + await umbracoApi.partialViews.ensureMacroFileNameNotExists(fileName); + } + + test('Create new partial view macro', async ({page, umbracoApi, umbracoUi}) => { + const name = "TestPartialViewMacro"; + + await cleanup(umbracoApi, name); + + await openPartialViewMacroCreatePanel(page, umbracoUi); + + await page.locator('.menu-label localize[key="create_newPartialViewMacro"]').click(); + + //Type name + await umbracoUi.setEditorHeaderName(name); + + //Save + await page.locator('.btn-success').click(); + + //Assert + await umbracoUi.isSuccessNotificationVisible(); + + //Clean up + await cleanup(umbracoApi, name); + }); + + test('Create new partial view macro without macro', async ({page, umbracoApi, umbracoUi}) => { + const name = "TestPartialMacrolessMacro"; + + await cleanup(umbracoApi, name); + + await openPartialViewMacroCreatePanel(page, umbracoUi); + + await page.locator('.menu-label >> nth=1').click(); + + // Type name + await umbracoUi.setEditorHeaderName(name); + + // Save + await page.locator('.btn-success').click(); + + // Assert + await umbracoUi.isSuccessNotificationVisible(); + + // Clean + await cleanup(umbracoApi, name); + }); + + test('Create new partial view macro from snippet', async ({page, umbracoApi, umbracoUi}) => { + const name = "TestPartialFromSnippet"; + + await cleanup(umbracoApi, name); + + await openPartialViewMacroCreatePanel(page, umbracoUi); + + await page.locator('.menu-label >> nth=2').click(); + + // Select snippet + await page.locator('.menu-label >> nth=1').click(); + + // Type name + await umbracoUi.setEditorHeaderName(name); + + // Save + await page.locator('.btn-success').click(); + + // Assert + await umbracoUi.isSuccessNotificationVisible(); + + // Clean + await cleanup(umbracoApi, name); + }); + + test('Delete partial view macro', async ({page, umbracoApi, umbracoUi}) => { + const name = "TestDeletePartialViewMacro"; + const fullName = name + ".cshtml" + + await cleanup(umbracoApi, name); + + const partialViewMacro = new PartialViewMacroBuilder() + .withName(name) + .withContent("@inherits Umbraco.Web.Macros.PartialViewMacroPage") + .build(); + + await umbracoApi.partialViews.save(partialViewMacro); + + // Navigate to settings + await umbracoUi.goToSection(ConstantHelper.sections.settings); + await umbracoUi.waitForTreeLoad(ConstantHelper.sections.settings); + + // Delete partialViewMacro + await umbracoUi.clickElement(umbracoUi.getTreeItem("settings", ["Partial View Macro Files", fullName]), {button: "right"}); + await umbracoUi.clickElement(umbracoUi.getContextMenuAction(ConstantHelper.actions.delete)); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.ok)); + + // Assert + await expect(await page.locator("body",{ hasText: fullName})).not.toBeVisible(); + + // Clean + await cleanup(umbracoApi, name); + }); + + test('Edit partial view macro', async ({page, umbracoApi, umbracoUi}) => { + const name = "TestPartialViewMacroEditable"; + const fullName = name + ".cshtml"; + + await cleanup(umbracoApi, name); + + const partialViewMacro = new PartialViewMacroBuilder() + .withName(name) + .withContent("@inherits Umbraco.Web.Macros.PartialViewMacroPage") + .build(); + + await umbracoApi.partialViews.save(partialViewMacro); + + // Navigate to settings + await umbracoUi.goToSection(ConstantHelper.sections.settings); + await umbracoUi.waitForTreeLoad(ConstantHelper.sections.settings); + await umbracoUi.clickElement(umbracoUi.getTreeItem("settings", ["Partial View Macro Files", fullName])); + + // Type an edit + await page.locator('.ace_text-input').type(" // test" ); + + // Save + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.save)); + + // Assert + await umbracoUi.isSuccessNotificationVisible(); + + await cleanup(umbracoApi, name); + }); +}); diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Settings/partialViews.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Settings/partialViews.spec.ts new file mode 100644 index 0000000000..6f5a358103 --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Settings/partialViews.spec.ts @@ -0,0 +1,144 @@ +import {expect} from '@playwright/test'; +import {ConstantHelper, test} from '@umbraco/playwright-testhelpers'; +import {PartialViewBuilder} from "@umbraco/json-models-builders"; + +test.describe('Partial Views', () => { + + test.beforeEach(async ({page, umbracoApi}) => { + // TODO: REMOVE THIS WHEN SQLITE IS FIXED + // Wait so we don't bombard the API + await page.waitForTimeout(1000); + await umbracoApi.login(); + }); + + async function openPartialViewsCreatePanel(page, umbracoUi) { + await navigateToSettings(page, umbracoUi) + await umbracoUi.clickElement(umbracoUi.getTreeItem("settings", ["Partial Views"]), {button: "right"}); + } + + async function navigateToSettings(page, umbracoUi) { + await umbracoUi.goToSection(ConstantHelper.sections.settings); + await umbracoUi.waitForTreeLoad(ConstantHelper.sections.settings); + } + + test('Create new empty partial view', async ({page, umbracoApi, umbracoUi}) => { + const name = "TestPartialView"; + const fileName = name + ".cshtml"; + + await umbracoApi.partialViews.ensureNameNotExists(fileName); + + await openPartialViewsCreatePanel(page, umbracoUi); + + await umbracoUi.clickElement(umbracoUi.getContextMenuAction(ConstantHelper.actions.create)); + await page.locator('.menu-label localize[key="create_newEmptyPartialView"]').click(); + + //Type name + await umbracoUi.setEditorHeaderName(name); + + //Save + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.save)); + + //Assert + await umbracoUi.isSuccessNotificationVisible(); + + //Clean up + await umbracoApi.partialViews.ensureNameNotExists(fileName); + }); + + test('Create partial view from snippet', async ({page, umbracoApi, umbracoUi}) => { + const name = "TestPartialViewFromSnippet"; + const fileName = name + ".cshtml"; + + await umbracoApi.partialViews.ensureNameNotExists(fileName); + + await openPartialViewsCreatePanel(page, umbracoUi); + + await umbracoUi.clickElement(umbracoUi.getContextMenuAction("action-create")); + await page.locator('.menu-label >> nth=1').click(); + // Select snippet + await page.locator('.menu-label >> nth=2').click(); + + // Type name + await umbracoUi.setEditorHeaderName(name); + + // Save + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.save)); + + // Assert + await umbracoUi.isSuccessNotificationVisible(); + + // Clean up + await umbracoApi.partialViews.ensureNameNotExists(fileName); + }); + + test('Partial view with no name', async ({page, umbracoApi, umbracoUi}) => { + await openPartialViewsCreatePanel(page, umbracoUi); + + await umbracoUi.clickElement(umbracoUi.getContextMenuAction("action-create")); + await page.locator('.menu-label localize[key="create_newEmptyPartialView"]').click(); + + // The test would fail intermittently, most likely because the editor didn't have time to load + // This should ensure that the editor is loaded and the test should no longer fail unexpectedly. + await expect(await page.locator('.ace_content')).toBeDefined(); + + // Click save + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.save)); + + // Asserts + await umbracoUi.isErrorNotificationVisible(); + }); + + test('Delete partial view', async ({page, umbracoApi, umbracoUi}) => { + const name = "TestDeletePartialView"; + const fileName = name + ".cshtml"; + + await umbracoApi.partialViews.ensureNameNotExists(fileName); + + // Build and save partial view + const partialView = new PartialViewBuilder() + .withName(name) + .withContent("@inherits UUmbraco.Cms.Web.Common.Views.UmbracoViewPage") + .build(); + + await umbracoApi.partialViews.save(partialView); + + await navigateToSettings(page, umbracoUi); + + // Delete partial view + await umbracoUi.clickElement(umbracoUi.getTreeItem("settings", ["Partial Views", fileName]), {button: "right"}); + await umbracoUi.clickElement(umbracoUi.getContextMenuAction(ConstantHelper.actions.delete)); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.ok)); + + // Assert + await expect(await page.locator("body", { hasText: fileName})).not.toBeVisible(); + + // Clean + await umbracoApi.partialViews.ensureNameNotExists(fileName); + }); + + test('Edit partial view', async ({page, umbracoApi, umbracoUi}) => { + const name = 'EditPartialView'; + const fileName = name + ".cshtml"; + + await umbracoApi.partialViews.ensureNameNotExists(fileName); + + const partialView = new PartialViewBuilder() + .withName(name) + .withContent("@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage\n") + .build(); + + await umbracoApi.partialViews.save(partialView); + + await navigateToSettings(page, umbracoUi); + // Open partial view + await umbracoUi.clickElement(umbracoUi.getTreeItem("settings", ["Partial Views", fileName])); + // Edit + await page.locator('.ace_text-input').type("var num = 5;"); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.save)); + + // Assert + await umbracoUi.isSuccessNotificationVisible(); + // Clean + await umbracoApi.partialViews.ensureNameNotExists(fileName); + }); +}); diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Settings/relationTypes.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Settings/relationTypes.spec.ts new file mode 100644 index 0000000000..d1a375ef6e --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Settings/relationTypes.spec.ts @@ -0,0 +1,41 @@ +import {expect} from '@playwright/test'; +import {ConstantHelper, test} from '@umbraco/playwright-testhelpers'; + +test.describe('Relation Types', () => { + + test.beforeEach(async ({page, umbracoApi}) => { + // TODO: REMOVE THIS WHEN SQLITE IS FIXED + // Wait so we don't bombard the API + await page.waitForTimeout(1000); + await umbracoApi.login(); + }); + + test('Create relation type', async ({page, umbracoApi, umbracoUi}) => { + const name = "Test relation type"; + + await umbracoApi.relationTypes.ensureNameNotExists(name); + + await umbracoUi.goToSection(ConstantHelper.sections.settings); + await umbracoUi.waitForTreeLoad(ConstantHelper.sections.settings); + + await umbracoUi.clickElement(umbracoUi.getTreeItem("settings", ["Relation Types"]), {button: "right"}); + + await umbracoUi.clickElement(umbracoUi.getContextMenuAction(ConstantHelper.actions.create)); + + const form = await page.locator('form[name="createRelationTypeForm"]'); + + await form.locator('input[name="relationTypeName"]').type(name); + await form.locator('[name="relationType-direction"] input').first().click({force: true}); + await page.selectOption('select[name="relationType-parent"]', {label: "Document"}); + await page.selectOption('select[name="relationType-child"]', {label: "Media"}); + await form.locator('[name="relationType-isdependency"]').last().click({force: true}); + await form.locator('.btn-primary').click(); + + await page.waitForNavigation(); + + expect(page.url()).toContain("#/settings/relationTypes/edit/"); + + //Clean up + await umbracoApi.relationTypes.ensureNameNotExists(name); + }); +}); diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Settings/scripts.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Settings/scripts.spec.ts new file mode 100644 index 0000000000..b1239acf30 --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Settings/scripts.spec.ts @@ -0,0 +1,120 @@ +import {ConstantHelper, test} from '@umbraco/playwright-testhelpers'; +import {expect} from "@playwright/test"; +import {ScriptBuilder} from "@umbraco/json-models-builders"; + +test.describe('Scripts', () => { + test.beforeEach(async ({ page, umbracoApi }) => { + // TODO: REMOVE THIS WHEN SQLITE IS FIXED + // Wait so we don't bombard the API + await page.waitForTimeout(1000); + await umbracoApi.login(); + }); + + test('Create new Javascript file', async ({ page, umbracoApi, umbracoUi }) => { + const name = "CreateNewScript"; + const fileName = name + ".js"; + + await umbracoApi.scripts.ensureNameNotExists(fileName); + + await umbracoUi.goToSection("settings"); + await umbracoUi.waitForTreeLoad('settings'); + + await umbracoUi.clickElement(umbracoUi.getTreeItem("settings", ["Scripts"]), { button: "right"}); + await umbracoUi.clickElement(umbracoUi.getContextMenuAction("action-create")); + await page.locator('.menu-label localize[key="create_newJavascriptFile"]').click(); + await page.waitForResponse('**/umbraco/lib/ace-builds/src-min-noconflict/worker-javascript.js'); + + await umbracoUi.setEditorHeaderName(name); + await page.locator('.btn-success').click(); + + await umbracoUi.isSuccessNotificationVisible(); + + expect(await umbracoApi.scripts.exists(fileName)).toBeTruthy(); + + await umbracoApi.scripts.ensureNameNotExists(fileName); + }); + + test('Delete a JavaScript file', async ({ page, umbracoApi, umbracoUi}) => { + const name = "TestDeleteScriptFile"; + const fileName = name + ".js"; + + await umbracoApi.scripts.ensureNameNotExists(fileName); + + const script = new ScriptBuilder() + .withName(name) + .withContent('alert("this is content");') + .build(); + + await umbracoApi.scripts.save(script); + + await umbracoUi.goToSection("settings"); + await umbracoUi.waitForTreeLoad('settings'); + + await umbracoUi.clickElement(umbracoUi.getTreeItem("settings", ["Scripts", fileName]), { button: "right"}); + await umbracoUi.clickElement(umbracoUi.getContextMenuAction("action-delete")); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey("general_ok")); + + await expect(await (page.locator(`text=${fileName}`))).toHaveCount(0); + expect(await (umbracoApi.scripts.exists(fileName))).toBeFalsy(); + }); + + test('Update JavaScript file', async ({ page, umbracoApi, umbracoUi }) => { + const name = "TestEditJavaScriptFile"; + const nameEdit = "Edited"; + const fileName = name + ".js"; + const editedFileName = name + nameEdit + ".js"; + await umbracoApi.scripts.ensureNameNotExists(fileName); + await umbracoApi.scripts.ensureNameNotExists(editedFileName); + + const originalContent = 'console.log("A script);\n'; + const edit = 'alert("content");'; + const expected = originalContent + edit; + + const script = new ScriptBuilder() + .withName(name) + .withContent(originalContent) + .build(); + + await umbracoApi.scripts.save(script); + + await umbracoUi.goToSection("settings"); + await umbracoUi.waitForTreeLoad('settings'); + await umbracoUi.clickElement(umbracoUi.getTreeItem("settings", ["Scripts", fileName])); + + await page.locator('.ace_text-input').type(edit); + + const header = await page.locator("#headerName"); + + await header.click(); + // The ui appends .js automagically, so we have to delete that before we can edit the name. + await page.keyboard.press("Backspace"); + await page.keyboard.press("Backspace"); + await page.keyboard.press("Backspace"); + await header.type(nameEdit); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.save)); + + await umbracoUi.isSuccessNotificationVisible(); + expect(await umbracoApi.scripts.verifyContent(editedFileName, expected)).toBeTruthy(); + + await umbracoApi.scripts.ensureNameNotExists(fileName); + await umbracoApi.scripts.ensureNameNotExists(editedFileName); + }); + + test('Can delete folder', async ({ page, umbracoApi, umbracoUi }) => { + + const folderName = "TestFolder"; + await umbracoApi.scripts.ensureNameNotExists(folderName); + await umbracoApi.scripts.saveFolder(folderName); + + await umbracoUi.goToSection(ConstantHelper.sections.settings); + await umbracoUi.waitForTreeLoad(ConstantHelper.sections.settings); + await umbracoUi.clickElement(umbracoUi.getTreeItem("settings", ["Scripts", folderName]), {button: "right"}); + await umbracoUi.clickElement(umbracoUi.getContextMenuAction(ConstantHelper.actions.delete)); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.ok)); + + await expect(await page.locator(`text=${folderName}`)).toHaveCount(0); + expect(await umbracoApi.scripts.exists(folderName)).toBeFalsy(); + + await umbracoApi.scripts.ensureNameNotExists(folderName); + }); +}); \ No newline at end of file diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Settings/stylesheets.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Settings/stylesheets.spec.ts new file mode 100644 index 0000000000..95fd0d98b0 --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Settings/stylesheets.spec.ts @@ -0,0 +1,67 @@ +import {expect} from '@playwright/test'; +import {ConstantHelper, test} from '@umbraco/playwright-testhelpers'; +import {StylesheetBuilder} from "@umbraco/json-models-builders"; + +test.describe('Stylesheets', () => { + + const name = "TestStylesheet"; + const fileName = name + ".css"; + + test.beforeEach(async ({page, umbracoApi}) => { + // TODO: REMOVE THIS WHEN SQLITE IS FIXED + // Wait so we don't bombard the API + await page.waitForTimeout(1000); + await umbracoApi.login(); + }); + + test.afterEach(async ({page, umbracoApi}) => { + await umbracoApi.stylesheets.ensureNameNotExists(name); + }); + + test('Create new style sheet file', async ({page, umbracoApi, umbracoUi}) => { + + await umbracoUi.goToSection(ConstantHelper.sections.settings); + await umbracoUi.waitForTreeLoad(ConstantHelper.sections.settings); + + await umbracoUi.clickElement(umbracoUi.getTreeItem("settings", ["Stylesheets"]), {button: "right"}); + + await umbracoUi.clickElement(umbracoUi.getContextMenuAction(ConstantHelper.actions.create)); + await page.locator('.menu-label').first().click(); // TODO: Would be better to use something like cy.umbracoContextMenuAction("action-mediaType").click(); + // We have to wait here till everything is loaded, or worker will throw error + await page.waitForResponse('**/umbraco/lib/ace-builds/src-min-noconflict/worker-css.js'); + + // Type name + await umbracoUi.setEditorHeaderName(name); + + // Save + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.save)); + + // Assert + await umbracoUi.isSuccessNotificationVisible(); + }); + + test('Deletes a stylesheet', async ({page, umbracoApi, umbracoUi}) => { + + const stylesheet = new StylesheetBuilder() + .withVirtualPath("/css/") + .withFileType("stylesheets") + .withName(name) + .build(); + + await umbracoApi.stylesheets.save(stylesheet); + + // Navigate to Settings section + await umbracoUi.goToSection(ConstantHelper.sections.settings); + await umbracoUi.waitForTreeLoad(ConstantHelper.sections.settings); + + // Open stylesheet tree + await umbracoUi.clickElement(umbracoUi.getTreeItem("settings", ["Stylesheets", name]), {button: "right"}); + + // Delete stylesheet + await umbracoUi.clickElement(umbracoUi.getContextMenuAction(ConstantHelper.actions.delete)); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.ok)); + + // Assert + await expect(await page.locator('.umb-tree-item__inner > .umb-tree-item__label >> text=' + name)).not.toBeVisible(); + }); +}); diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Settings/templates.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Settings/templates.spec.ts new file mode 100644 index 0000000000..a31ed0a5a8 --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Settings/templates.spec.ts @@ -0,0 +1,188 @@ +import {expect} from '@playwright/test'; +import {ConstantHelper, test} from '@umbraco/playwright-testhelpers'; +import {TemplateBuilder} from "@umbraco/json-models-builders"; + +test.describe('Templates', () => { + test.beforeEach(async ({page, umbracoApi}) => { + // TODO: REMOVE THIS WHEN SQLITE IS FIXED + // Wait so we don't bombard the API + await page.waitForTimeout(1000); + await umbracoApi.login(); + }); + + async function navigateToSettings(page, umbracoUi) { + await umbracoUi.goToSection(ConstantHelper.sections.settings); + await umbracoUi.waitForTreeLoad(ConstantHelper.sections.settings); + } + + async function createTemplate(page, umbracoUi) { + await navigateToSettings(page, umbracoUi); + await umbracoUi.clickElement(umbracoUi.getTreeItem("settings", ["Templates"]), {button: "right"}); + await umbracoUi.clickElement(umbracoUi.getContextMenuAction(ConstantHelper.actions.create)); + } + + test('Create template', async ({page, umbracoApi, umbracoUi}) => { + const name = "Create template test"; + await umbracoApi.templates.ensureNameNotExists(name); + + await createTemplate(page, umbracoUi); + // We have to wait for the ace editor to load, because when the editor is loading it will "steal" the focus briefly, + // which causes the save event to fire if we've added something to the header field, causing errors. + await page.waitForTimeout(500); + + // Type name + await umbracoUi.getEditorHeaderName(name) + // Save + // We must drop focus for the auto save event to occur. + await page.focus('.btn-success'); + // And then wait for the auto save event to finish by finding the page in the tree view. + // This is a bit of a roundabout way to find items in a tree view since we dont use umbracoTreeItem + // but we must be able to wait for the save event to finish, and we can't do that with umbracoTreeItem + const label = await page.locator("a:has-text('Create template test')"); + await expect(label).toBeVisible({timeout: 10000}); + // Now that the auto save event has finished we can save + // and there wont be any duplicates or file in use errors. + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.save)); + //Assert + await umbracoUi.isSuccessNotificationVisible(); + // For some reason cy.umbracoErrorNotification tries to click the element which is not possible + // if it doesn't actually exist, making should('not.be.visible') impossible. + await expect(await page.locator('.umb-notifications__notifications > .alert-error')).not.toBeVisible(); + + // Clean up + await umbracoApi.templates.ensureNameNotExists(name); + }); + + test('Unsaved changes stay', async ({page, umbracoApi, umbracoUi}) => { + const name = "Templates Unsaved Changes Stay test"; + const edit = "var num = 5;"; + await umbracoApi.templates.ensureNameNotExists(name); + + const template = new TemplateBuilder() + .withName(name) + .withContent('@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage\n') + .build(); + + await umbracoApi.templates.saveTemplate(template); + + await navigateToSettings(page, umbracoUi); + + // Open partial view + await umbracoUi.clickElement(umbracoUi.getTreeItem("settings", ["Templates", name])); + // Edit + await page.locator('.ace_content').type(edit); + await expect(await page.locator('.ace_content')).toBeVisible(); + await expect(await page.locator('.btn-success')).toBeVisible(); + + // Navigate away + await umbracoUi.goToSection(ConstantHelper.sections.content); + await umbracoUi.waitForTreeLoad(ConstantHelper.sections.content); + + // Click stay button + await page.locator('umb-button[label="Stay"] button:enabled').click(); + + // Assert + // That the same document is open + await expect(await page.locator('#headerName')).toHaveValue(name); + await expect(await page.locator('.ace_content')).toContainText(edit); + + // Clean up + await umbracoApi.templates.ensureNameNotExists(name); + }); + + test('Discard unsaved changes', async ({page, umbracoApi, umbracoUi}) => { + const name = "Discard changes test"; + const edit = "var num = 5;"; + + await umbracoApi.templates.ensureNameNotExists(name); + + const template = new TemplateBuilder() + .withName(name) + .withContent('@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage\n') + .build(); + + await umbracoApi.templates.saveTemplate(template); + + await navigateToSettings(page, umbracoUi); + + // Open partial view + await umbracoUi.clickElement(umbracoUi.getTreeItem("settings", ["Templates", name])); + // Edit + await page.locator('.ace_content').type(edit); + await expect(await page.locator('.ace_content')).toBeVisible(); + await expect(await page.locator('.btn-success')).toBeVisible(); + + // Navigate away + await umbracoUi.goToSection(ConstantHelper.sections.content); + await umbracoUi.waitForTreeLoad(ConstantHelper.sections.content); + // Click discard + await page.locator('umb-button[label="Discard changes"] button:enabled').click(); + // Navigate back + await navigateToSettings(page, umbracoUi); + + // Asserts + await expect(await page.locator('.ace_content')).not.toContainText(edit); + // cy.umbracoPartialViewExists(fileName).then(exists => { expect(exists).to.be.false; }); TODO: Switch to template + await umbracoApi.templates.ensureNameNotExists(name); + }); + + test('Insert macro', async ({page, umbracoApi, umbracoUi}) => { + const name = 'InsertMacroTest'; + + await umbracoApi.templates.ensureNameNotExists(name); + await umbracoApi.macros.ensureNameNotExists(name); + + const template = new TemplateBuilder() + .withName(name) + .withContent('') + .build(); + + await umbracoApi.templates.saveTemplate(template); + + await umbracoApi.macros.save(name); + + await navigateToSettings(page, umbracoUi); + await umbracoUi.clickElement(umbracoUi.getTreeItem("settings", ["Templates", name])); + // Insert macro + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.insert)); + + await page.locator('.umb-insert-code-box__title >> text=Macro').click(); + await page.locator(`.umb-card-grid-item[title='${name}']`).click(); + + // Assert + await expect(await page.locator('.ace_content')).toContainText('@await Umbraco.RenderMacroAsync("' + name + '")'); + + // Clean + await umbracoApi.templates.ensureNameNotExists(name); + await umbracoApi.macros.ensureNameNotExists(name); + }); + + test('Insert value', async ({page, umbracoApi, umbracoUi}) => { + const name = 'Insert Value Test'; + + await umbracoApi.templates.ensureNameNotExists(name); + + const partialView = new TemplateBuilder() + .withName(name) + .withContent('') + .build(); + + await umbracoApi.templates.saveTemplate(partialView); + + await navigateToSettings(page, umbracoUi); + await umbracoUi.clickElement(umbracoUi.getTreeItem("settings", ["Templates", name])); + + // Insert value + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.insert)); + await page.locator('.umb-insert-code-box__title >> text=Value').click(); + + await page.selectOption('select', 'umbracoBytes'); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.submit)); + + // assert + await expect(await page.locator('.ace_content')).toContainText('@Model.Value("umbracoBytes")'); + + // Clean + await umbracoApi.templates.ensureNameNotExists(name); + }); +}); diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Tabs/tabs.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Tabs/tabs.spec.ts new file mode 100644 index 0000000000..cff2a2ba7b --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Tabs/tabs.spec.ts @@ -0,0 +1,503 @@ +import { expect, Page } from '@playwright/test'; +import {test, ApiHelpers, UiHelpers, AliasHelper, ConstantHelper} from '@umbraco/playwright-testhelpers'; +import { DocumentTypeBuilder } from "@umbraco/json-models-builders"; + +const tabsDocTypeName = 'Tabs Test Document'; +const tabsDocTypeAlias = AliasHelper.toAlias(tabsDocTypeName); + +test.describe('Tabs', () => { + + test.beforeEach(async ({ umbracoApi, page }) => { + // TODO: REMOVE THIS WHEN SQLITE IS FIXED + // Wait so we don't bombard the API + await page.waitForTimeout(1000); + await umbracoApi.login(); + }); + + test.afterEach(async ({page, umbracoApi}) => { + await umbracoApi.documentTypes.ensureNameNotExists(tabsDocTypeName); + await umbracoApi.templates.ensureNameNotExists(tabsDocTypeName) + }); + + async function openDocTypeFolder(umbracoUi: UiHelpers, page: Page) { + await umbracoUi.goToSection('settings'); + await umbracoUi.waitForTreeLoad('settings'); + await page.locator('.umb-tree-item__inner > .umb-tree-item__arrow').first().click(); + await page.locator(`a:has-text("${tabsDocTypeName}")`).click(); + } + + async function createDocTypeWithTabsAndNavigate(umbracoUi: UiHelpers, umbracoApi: ApiHelpers, page: Page){ + await umbracoApi.documentTypes.ensureNameNotExists(tabsDocTypeName); + await umbracoApi.content.deleteAllContent(); + const tabsDocType = new DocumentTypeBuilder() + .withName(tabsDocTypeName) + .withAlias(tabsDocTypeAlias) + .withAllowAsRoot(true) + .withDefaultTemplate(tabsDocTypeAlias) + .addTab() + .withName('Tab 1') + .addGroup() + .withName('Tab group') + .addUrlPickerProperty() + .withAlias("urlPicker") + .done() + .done() + .done() + .build(); + await umbracoApi.documentTypes.save(tabsDocType); + await openDocTypeFolder(umbracoUi, page); + } + + test('Create tab', async ({umbracoUi, umbracoApi, page}) => { + await umbracoApi.documentTypes.ensureNameNotExists(tabsDocTypeName); + await umbracoApi.content.deleteAllContent(); + const tabsDocType = new DocumentTypeBuilder() + .withName(tabsDocTypeName) + .withAlias(tabsDocTypeAlias) + .withAllowAsRoot(true) + .withDefaultTemplate(tabsDocTypeAlias) + .addGroup() + .withName('Tabs1Group') + .addUrlPickerProperty() + .withAlias('picker') + .done() + .done() + .build(); + await umbracoApi.documentTypes.save(tabsDocType); + + await umbracoUi.goToSection('settings'); + await umbracoUi.waitForTreeLoad('settings'); + + await umbracoUi.clickElement(umbracoUi.getTreeItem("settings", ["Document Types", tabsDocTypeName])) + // Create a tab + await page.locator('.umb-group-builder__tabs__add-tab').click(); + await page.locator('ng-form.ng-invalid > .umb-group-builder__group-title-input').fill('Tab 1'); + // Create a 2nd tab manually + await page.locator('.umb-group-builder__tabs__add-tab').click(); + await page.locator('ng-form.ng-invalid > .umb-group-builder__group-title-input').fill('Tab 2'); + // Create a textstring property + await page.locator('[aria-hidden="false"] > .umb-box-content > .umb-group-builder__group-add-property').click(); + await page.locator('.editor-label').fill('property name'); + await page.locator('[data-element="editor-add"]').click(); + + // Search for textstring + await page.locator('#datatype-search').fill('Textstring'); + + // Choose first item + await page.locator('[title="Textstring"]').first().click(); + + // Save property + await page.locator('.btn-success').last().click(); + await (await umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.save)).click(); + //Assert + await umbracoUi.isSuccessNotificationVisible(); + await expect(page.locator('[title="tab1"]').first()).toBeVisible(); + await expect(page.locator('[title="tab2"]').first()).toBeVisible(); + }); + + test('Delete tabs', async ({umbracoUi, umbracoApi, page}) => { + await createDocTypeWithTabsAndNavigate(umbracoUi, umbracoApi, page); + + // Check if tab is there, else if it wasn't created, this test would always pass + let tab = await page.locator('[title="aTab 1"]'); + await expect(tab.first()).toBeVisible(); + + // Delete a tab + await page.locator('.btn-reset > [icon="icon-trash"]').first().click(); + await page.locator('.umb-button > .btn').last().click(); + await (await umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.save)).click(); + // Assert + await umbracoUi.isSuccessNotificationVisible(); + let deletedTab = await page.locator('[title="aTab 1"]'); + await expect(deletedTab.first()).not.toBeVisible(); + // Clean + await umbracoApi.documentTypes.ensureNameNotExists(tabsDocTypeName); + }); + + test('Delete property in a tab', async ({umbracoUi, umbracoApi, page}) => { + await umbracoApi.documentTypes.ensureNameNotExists(tabsDocTypeName); + const tabsDocType = new DocumentTypeBuilder() + .withName(tabsDocTypeName) + .withAlias(tabsDocTypeAlias) + .withAllowAsRoot(true) + .withDefaultTemplate(tabsDocTypeAlias) + .addTab() + .withName('Tab 1') + .addGroup() + .withName('Tab group') + .addUrlPickerProperty() + .withAlias("urlPicker") + .done() + .addContentPickerProperty() + .withAlias('picker') + .done() + .done() + .done() + .build(); + await umbracoApi.documentTypes.save(tabsDocType); + await openDocTypeFolder(umbracoUi, page); + await page.locator('[aria-label="Delete property"]').last().click(); + await (await umbracoUi.getButtonByLabelKey('actions_delete')).click(); + await (await umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.save)).click() + // Assert + await umbracoUi.isSuccessNotificationVisible(); + await expect(await page.locator('[title=urlPicker]')).toBeVisible(); + await expect(await page.locator('[title=picker]')).toHaveCount(0); + }); + + test('Delete group in tab', async ({umbracoUi, umbracoApi, page}) => { + await umbracoApi.documentTypes.ensureNameNotExists(tabsDocTypeName); + const tabsDocType = new DocumentTypeBuilder() + .withName(tabsDocTypeName) + .withAlias(tabsDocTypeAlias) + .withAllowAsRoot(true) + .withDefaultTemplate(tabsDocTypeAlias) + .addTab() + .withName('Tab 1') + .addGroup() + .withName('Tab group') + .addUrlPickerProperty() + .withAlias("urlPicker") + .done() + .done() + .addGroup() + .withName('Content Picker Group') + .addContentPickerProperty() + .withAlias('picker') + .done() + .done() + .done() + .build(); + await umbracoApi.documentTypes.save(tabsDocType); + await openDocTypeFolder(umbracoUi, page); + // Delete group + await page.locator(':nth-match(.umb-group-builder__group-remove > [icon="icon-trash"], 2)').click(); + await (await umbracoUi.getButtonByLabelKey('actions_delete')).click(); + await (await umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.save)).click() + // Assert + await umbracoUi.isSuccessNotificationVisible(); + await expect(await page.locator('[title=picker]')).toBeVisible(); + await expect(await page.locator('[title=urlPicker]')).toHaveCount(0); + }); + + test('Reorders tab', async ({umbracoUi, umbracoApi, page}) => { + await umbracoApi.documentTypes.ensureNameNotExists(tabsDocTypeName); + + const tabsDocType = new DocumentTypeBuilder() + .withName(tabsDocTypeName) + .withAlias(tabsDocTypeAlias) + .withAllowAsRoot(true) + .withDefaultTemplate(tabsDocTypeAlias) + .addTab() + .withName('Tab 1') + .addGroup() + .withName('Tab group 1') + .addUrlPickerProperty() + .withLabel('Url picker 1') + .withAlias("urlPicker") + .done() + .done() + .done() + .addTab() + .withName('Tab 2') + .addGroup() + .withName('Tab group 2') + .addUrlPickerProperty() + .withLabel('Url picker 2') + .withAlias("pickerTab 2") + .done() + .done() + .done() + .addTab() + .withName('Tab 3') + .addGroup() + .withName('Tab group') + .addUrlPickerProperty() + .withLabel('Url picker 3') + .withAlias('pickerTab3') + .done() + .done() + .done() + .build(); + + await umbracoApi.documentTypes.save(tabsDocType); + await openDocTypeFolder(umbracoUi, page); + // Check if there are any tabs + await page.locator('[alias="reorder"]').click(); + // Type order in + await page.locator('.umb-group-builder__tab-sort-order > .umb-property-editor-tiny').first().fill('3'); + await page.locator('[alias="reorder"]').click(); + // Assert + await expect(await page.locator('[ui-sortable="sortableOptionsTab"]').locator("xpath=/*[1]")).toHaveAttribute('data-tab-alias', 'aTab 2'); + await expect(await page.locator('[ui-sortable="sortableOptionsTab"]').locator("xpath=/*[2]")).toHaveAttribute('data-tab-alias', 'aTab 3'); + await expect(await page.locator('[ui-sortable="sortableOptionsTab"]').locator("xpath=/*[3]")).toHaveAttribute('data-tab-alias', 'aTab 1'); + }); + + test('Reorders groups in a tab', async ({umbracoUi, umbracoApi, page}) => { + await umbracoApi.documentTypes.ensureNameNotExists(tabsDocTypeName); + const tabsDocType = new DocumentTypeBuilder() + .withName(tabsDocTypeName) + .withAlias(tabsDocTypeAlias) + .withAllowAsRoot(true) + .withDefaultTemplate(tabsDocTypeAlias) + .addTab() + .withName('Tab 1') + .addGroup() + .withName('Tab group 1') + .addUrlPickerProperty() + .withLabel('Url picker 1') + .withAlias("urlPicker") + .done() + .done() + .addGroup() + .withName('Tab group 2') + .addUrlPickerProperty() + .withLabel('Url picker 2') + .withAlias('urlPickerTwo') + .done() + .done() + .done() + .build(); + await umbracoApi.documentTypes.save(tabsDocType); + await openDocTypeFolder(umbracoUi, page); + await page.locator('[alias="reorder"]').click(); + await page.locator('.umb-property-editor-tiny >> nth=2').fill('1'); + + await page.locator('[alias="reorder"]').click(); + await (await umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.save)).click(); + // Assert + await umbracoUi.isSuccessNotificationVisible(); + await expect(await page.locator('.umb-group-builder__group-title-input >> nth=2')).toHaveAttribute('title', 'aTab 1/aTab group 2'); + }); + + test('Reorders properties in a tab', async ({umbracoUi, umbracoApi, page}) => { + await umbracoApi.documentTypes.ensureNameNotExists(tabsDocTypeName); + const tabsDocType = new DocumentTypeBuilder() + .withName(tabsDocTypeName) + .withAlias(tabsDocTypeAlias) + .withAllowAsRoot(true) + .withDefaultTemplate(tabsDocTypeAlias) + .addTab() + .withName('Tab 1') + .addGroup() + .withName('Tab group') + .addUrlPickerProperty() + .withLabel('PickerOne') + .withAlias("urlPicker") + .done() + .addUrlPickerProperty() + .withLabel('PickerTwo') + .withAlias('urlPickerTwo') + .done() + .done() + .done() + .build(); + await umbracoApi.documentTypes.save(tabsDocType); + await openDocTypeFolder(umbracoUi, page); + // Reorder + await page.locator('[alias="reorder"]').click(); + await page.locator('.umb-group-builder__group-sort-value').first().fill('2'); + await page.locator('[alias="reorder"]').click(); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.save)); + // Assert + await umbracoUi.isSuccessNotificationVisible(); + await expect(await page.locator('.umb-locked-field__input').last()).toHaveAttribute('title', 'urlPicker'); + }); + + test('Tab name cannot be empty', async ({umbracoUi, umbracoApi, page}) => { + await createDocTypeWithTabsAndNavigate(umbracoUi, umbracoApi, page); + await page.locator('.umb-group-builder__group-title-input').first().fill(""); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.save)); + //Assert + await umbracoUi.isErrorNotificationVisible(); + }); + + test('Two tabs cannot have the same name', async ({umbracoUi, umbracoApi, page}) => { + await umbracoApi.documentTypes.ensureNameNotExists(tabsDocTypeName); + const tabsDocType = new DocumentTypeBuilder() + .withName(tabsDocTypeName) + .withAlias(tabsDocTypeAlias) + .withAllowAsRoot(true) + .withDefaultTemplate(tabsDocTypeAlias) + .addTab() + .withName('Tab 1') + .addGroup() + .withName('Tab group') + .addUrlPickerProperty() + .withAlias("urlPicker") + .done() + .done() + .done() + .build(); + await umbracoApi.documentTypes.save(tabsDocType); + await openDocTypeFolder(umbracoUi, page); + // Create a 2nd tab manually + await page.locator('.umb-group-builder__tabs__add-tab').click(); + await page.locator('ng-form.ng-invalid > .umb-group-builder__group-title-input').fill('Tab 1'); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.save)); + // Assert + await umbracoUi.isErrorNotificationVisible(); + }); + + test('Group name cannot be empty', async ({umbracoUi, umbracoApi, page}) => { + await createDocTypeWithTabsAndNavigate(umbracoUi, umbracoApi, page); + await page.locator('.clearfix > .-placeholder').click(); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.save)); + // Assert + await umbracoUi.isErrorNotificationVisible(); + }); + + test('Group name cannot have the same name', async ({umbracoUi, umbracoApi, page}) => { + await createDocTypeWithTabsAndNavigate(umbracoUi, umbracoApi, page); + await page.locator('.clearfix > .-placeholder').click(); + await page.locator('.umb-group-builder__group-title-input').last().type('Tab group'); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.save)); + // Assert + await umbracoUi.isErrorNotificationVisible(); + }); + + test('Drag a group into another tab', async ({umbracoUi, umbracoApi, page}) => { + await umbracoApi.documentTypes.ensureNameNotExists(tabsDocTypeName); + const tabsDocType = new DocumentTypeBuilder() + .withName(tabsDocTypeName) + .withAlias(tabsDocTypeAlias) + .withAllowAsRoot(true) + .withDefaultTemplate(tabsDocTypeAlias) + .addTab() + .withName('Tab 1') + .addGroup() + .withName('Tab group') + .addUrlPickerProperty() + .withAlias("urlPicker") + .done() + .done() + .done() + .addTab() + .withName('Tab 2') + .addGroup() + .withName('Tab group tab 2') + .addUrlPickerProperty() + .withAlias('urlPickerTabTwo') + .done() + .done() + .addGroup() + .withName('Tab group 2') + .addUrlPickerProperty() + .withAlias('urlPickerTwo') + .done() + .done() + .done() + .build(); + await umbracoApi.documentTypes.save(tabsDocType); + await openDocTypeFolder(umbracoUi, page); + await page.locator('[alias="reorder"]').click(); + await page.locator('.umb-group-builder__tab').last().click(); + await page.locator('.umb-group-builder__group-title-icon').last().hover(); + await page.mouse.down(); + await page.locator('.umb-group-builder__tab >> nth=1').hover({force: true}); + await page.waitForTimeout(2000); + await page.mouse.up(); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.save)); + // Assert + await umbracoUi.isSuccessNotificationVisible(); + await expect(await page.locator('[title="aTab 1/aTab group 2"]')).toBeVisible(); + }); + + test('Drag and drop reorders a tab', async ({umbracoUi, umbracoApi, page}) => { + await umbracoApi.documentTypes.ensureNameNotExists(tabsDocTypeName); + const tabsDocType = new DocumentTypeBuilder() + .withName(tabsDocTypeName) + .withAlias(tabsDocTypeAlias) + .withAllowAsRoot(true) + .withDefaultTemplate(tabsDocTypeAlias) + .addTab() + .withName('Tab 1') + .addGroup() + .withName('Tab group') + .addUrlPickerProperty() + .withAlias("urlPicker") + .done() + .done() + .done() + .addTab() + .withName('Tab 2') + .addGroup() + .withName('Tab group tab 2') + .addUrlPickerProperty() + .withAlias('urlPickerTabTwo') + .done() + .done() + .addGroup() + .withName('Tab group 2') + .addUrlPickerProperty() + .withAlias('urlPickerTwo') + .done() + .done() + .done() + .build(); + await umbracoApi.documentTypes.save(tabsDocType); + await openDocTypeFolder(umbracoUi, page); + await page.locator('[alias="reorder"]').click(); + + await page.locator('.umb-group-builder__tab-title-icon >> nth=1').last().hover(); + await page.mouse.down(); + await page.locator('.umb-group-builder__tab >> nth=1').hover({force: true, position: {x: 0, y:10}}); + await page.waitForTimeout(2000); + await page.mouse.up(); + await page.locator('[alias="reorder"]').click(); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.save)); + //Assert + await umbracoUi.isSuccessNotificationVisible(); + await expect(await page.locator('[title="aTab 2"]').first()).toBeVisible(); + }); + + test('Drags and drops a group and converts to tab', async ({umbracoUi, umbracoApi, page}) => { + await umbracoApi.documentTypes.ensureNameNotExists(tabsDocTypeName); + const tabsDocType = new DocumentTypeBuilder() + .withName(tabsDocTypeName) + .withAlias(tabsDocTypeAlias) + .withAllowAsRoot(true) + .withDefaultTemplate(tabsDocTypeAlias) + .addTab() + .withName('Tab 1') + .addGroup() + .withName('Tab group') + .addUrlPickerProperty() + .withAlias("urlPicker") + .withLabel('UrlPickerOne') + .done() + .done() + .addGroup() + .withName('Tab group 2') + .addUrlPickerProperty() + .withAlias('urlPickerTwo') + .withLabel('UrlPickerTwo') + .done() + .done() + .done() + .addTab() + .withName('Tab 2') + .addGroup() + .withName('Tab group tab 2') + .addUrlPickerProperty() + .withAlias('urlPickerTabTwo') + .withLabel('UrlPickerTabTwo') + .done() + .done() + .done() + .build(); + await umbracoApi.documentTypes.save(tabsDocType); + await openDocTypeFolder(umbracoUi, page); + await page.locator('[alias="reorder"]').click(); + + await page.locator('.umb-group-builder__group-title-icon >> nth=1').last().hover(); + await page.mouse.down(); + await page.locator('.umb-group-builder__convert-dropzone').hover({force: true, position: {x: 0, y:10}}); + await page.waitForTimeout(2000); + await page.mouse.up(); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.save)); + //Assert + await umbracoUi.isSuccessNotificationVisible(); + await expect(await page.locator('[title="tabGroup"]').first()).toBeVisible(); + }); +}); \ No newline at end of file diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Tour/tours.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Tour/tours.spec.ts new file mode 100644 index 0000000000..b112887ff1 --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Tour/tours.spec.ts @@ -0,0 +1,101 @@ +import {expect} from '@playwright/test'; +import {test} from '@umbraco/playwright-testhelpers'; + +test.describe('Tours', () => { + const timeout = 60000; + test.beforeEach(async ({page, umbracoApi}) => { + // TODO: REMOVE THIS WHEN SQLITE IS FIXED + // Wait so we don't bombard the API + await page.waitForTimeout(1000); + await umbracoApi.login(); + await resetTourData(umbracoApi); + }); + + test.afterEach(async ({page, umbracoApi}) => { + await resetTourData(umbracoApi); + }); + + async function getPercentage(percentage, timeout, page) { + await expect(await page.locator('[data-element="help-tours"] .umb-progress-circle', {timeout: timeout})).toContainText(percentage + '%'); + } + + async function resetTourData(umbracoApi) { + const tourStatus = + { + "alias": "umbIntroIntroduction", + "completed": false, + "disabled": false + }; + + const response = await umbracoApi.post(process.env.URL + "/umbraco/backoffice/UmbracoApi/CurrentUser/PostSetUserTour", tourStatus) + } + + async function runBackOfficeIntroTour(percentageComplete, buttonText, timeout, page, umbracoUi) { + await expect(await page.locator('[data-element="help-tours"]')).toBeVisible(); + await umbracoUi.clickElement(page.locator('[data-element="help-tours"]')); + await expect(await page.locator('.umb-progress-circle', {timeout: timeout})).toContainText(percentageComplete + '%'); + + + await page.locator('[data-element="help-tours"]').click(); + await expect(await page.locator('[data-element="tour-umbIntroIntroduction"] .umb-button')).toBeVisible(); + await expect(await page.locator('[data-element="tour-umbIntroIntroduction"] .umb-button')).toContainText(buttonText); + await umbracoUi.clickElement(await page.locator('[data-element="tour-umbIntroIntroduction"] .umb-button')); + + //act + await expect(await page.locator('.umb-tour-step', {timeout: timeout})).toBeVisible(); + await expect(await page.locator('.umb-tour-step__footer')).toBeVisible(); + await expect(await page.locator('.umb-tour-step__counter')).toBeVisible(); + + for (let i = 1; i < 8; i++) { + + if (i == 4) { + continue + } + + await expect(await page.locator('.umb-tour-step__counter')).toContainText(i + '/13'); + await expect(await page.locator('.umb-tour-step__footer .umb-button')).toBeVisible(); + await umbracoUi.clickElement(page.locator('.umb-tour-step__footer .umb-button')); + } + await umbracoUi.clickElement(await umbracoUi.getGlobalUser()); + await expect(await page.locator('.umb-tour-step__counter', {timeout: timeout})).toContainText('9/13'); + await expect(await page.locator('.umb-tour-step__footer .umb-button')).toBeVisible(); + await umbracoUi.clickElement(page.locator('.umb-tour-step__footer .umb-button')); + await expect(await page.locator('.umb-tour-step__counter', {timeout: timeout})).toContainText('10/13'); + await expect(await page.locator('[data-element~="overlay-user"] [data-element="button-overlayClose"]')).toBeVisible(); + await umbracoUi.clickElement(page.locator('[data-element~="overlay-user"] [data-element="button-overlayClose"]')); + await expect(await page.locator('.umb-tour-step__counter', {timeout: timeout})).toContainText('11/13'); + await umbracoUi.clickElement(await umbracoUi.getGlobalHelp()); + + for (let i = 12; i < 13; i++) { + await expect(await page.locator('.umb-tour-step__counter', {timeout: timeout})).toContainText(i + '/13'); + await expect(await page.locator('.umb-tour-step__footer .umb-button')).toBeVisible(); + await umbracoUi.clickElement(page.locator('.umb-tour-step__footer .umb-button')); + } + await expect(await page.locator('.umb-tour-step__footer .umb-button')).toBeVisible(); + await umbracoUi.clickElement(page.locator('.umb-tour-step__footer .umb-button')); + await expect(await umbracoUi.getGlobalHelp()).toBeVisible(); + await umbracoUi.clickElement(page.locator('[label="Complete"]')); + + } + + test('Backoffice introduction tour should run', async ({page, umbracoApi, umbracoUi}) => { + // We have to reload this page, as we already get a page context after login + // before we have reset a users tour data + await expect(await umbracoUi.getGlobalHelp()).toBeVisible(); + await umbracoUi.clickElement(umbracoUi.getGlobalHelp()); + await runBackOfficeIntroTour(0, 'Start', timeout, page, umbracoUi); + + await expect(await page.locator('[data-element="help-tours"]')).toBeVisible(); + await getPercentage(17, timeout, page); + }); + + test('Backoffice introduction tour should run, then rerun', async ({page, umbracoApi, umbracoUi}) => { + await expect(await umbracoUi.getGlobalHelp()).toBeVisible(); + await umbracoUi.clickElement(umbracoUi.getGlobalHelp()); + await runBackOfficeIntroTour(0, 'Start', timeout, page, umbracoUi); + await runBackOfficeIntroTour(17, 'Rerun', timeout, page, umbracoUi); + + await expect(await umbracoUi.getGlobalHelp()).toBeVisible(); + await getPercentage(17, timeout, page); + }); +}); \ No newline at end of file diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Users/userGroups.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Users/userGroups.spec.ts new file mode 100644 index 0000000000..901973af11 --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Users/userGroups.spec.ts @@ -0,0 +1,93 @@ +import { expect, Page } from '@playwright/test'; +import { test, ApiHelpers, UiHelpers, AliasHelper } from '@umbraco/playwright-testhelpers'; +import { UserGroupBuilder } from '@umbraco/json-models-builders'; + +test.describe('User groups', () => { + + async function navigateToUserGroups(umbracoUi : UiHelpers, page : Page) { + await umbracoUi.goToSection('users'); + await page.locator('[data-element="sub-view-userGroups"]').click(); + } + + test.beforeEach(async ({ umbracoApi, page }) => { + // TODO: REMOVE THIS WHEN SQLITE IS FIXED + // Wait so we don't bombard the API + await page.waitForTimeout(1000); + await umbracoApi.login(); + }); + + test('Create user group', async ({umbracoUi, umbracoApi, page}) => { + const name = "Test Group"; + + await umbracoApi.userGroups.ensureNameNotExits(name); + + await navigateToUserGroups(umbracoUi, page); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey("actions_createGroup")); + + //Type name + await umbracoUi.getEditorHeaderName(name); + + // Assign sections + await page.locator('.umb-box:nth-child(1) .umb-property:nth-child(1) localize').click(); + + await umbracoUi.clickMultiple(page.locator('.umb-tree-item__inner')); + await page.locator('.btn-success').last().click(); + + // Save + await page.locator('.btn-success').click(); + + //Assert + await umbracoUi.isSuccessNotificationVisible(); + + //Clean up + await umbracoApi.userGroups.ensureNameNotExits(name); + }); + + test('Can delete user group', async ({umbracoUi, umbracoApi, page}) => { + + // Create user group + const groupName = "Delete user group test" + await umbracoApi.userGroups.ensureNameNotExits(groupName); + + const userGroup = new UserGroupBuilder() + .withName(groupName) + .build(); + + await umbracoApi.userGroups.save(userGroup); + await navigateToUserGroups(umbracoUi, page); + + // Delete the user group + await page.locator('.umb-table-body > :nth-child(2)').click(); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey("general_delete")); + await page.locator('umb-button[alias="overlaySubmit"]').click(); + + await umbracoUi.isSuccessNotificationVisible(); + await expect(await page.locator('.umb-table-body')).not.toHaveText(groupName); + + // Clean up + await umbracoApi.userGroups.ensureNameNotExits(groupName); + }); + + test('Cannot delete required groups', async ({umbracoUi, umbracoApi, page}) => { + + await navigateToUserGroups(umbracoUi, page); + + // There's not really a good way to be 100% sure we'll get the admin group, it should be first, but who knows + // so double check that we actually got the correct one + const administrators = await page.locator('.umb-table-body > :nth-child(1)'); + await expect(administrators).toContainText('Administrators'); + await administrators.click({force: true}); + + const sensitive = await page.locator('.umb-table-body > :nth-child(3)'); + await expect(sensitive).toContainText('Sensitive data'); + await sensitive.click({force: true}); + + const translators = await page.locator('.umb-table-body > :nth-child(4)'); + await expect(translators).toContainText('Translators'); + await translators.click({force: true}); + + // Now that we've clicked all that we shouldn't be able to delete, ensure that the delete button does not show up + let header = await page.locator('.umb-editor-sub-header'); + await expect(header).not.toContainText('Delete'); + }); +}); \ No newline at end of file diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Users/users.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Users/users.spec.ts new file mode 100644 index 0000000000..69c8021d72 --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Users/users.spec.ts @@ -0,0 +1,99 @@ +import { expect, Page } from '@playwright/test'; +import { test, ApiHelpers, UiHelpers, AliasHelper, ConstantHelper } from '@umbraco/playwright-testhelpers'; + +test.describe('Users', () => { + + const name = "Alice Bobson"; + const email = "alice-bobson@acceptancetest.umbraco"; + const startContentIds = []; + const startMediaIds = []; + const userGroups = ["admin"]; + + const userData = + { + "id": -1, + "parentId": -1, + "name": name, + "username": email, + "culture": "en-US", + "email": email, + "startContentIds": startContentIds, + "startMediaIds": startMediaIds, + "userGroups": userGroups, + "message": "" + }; + + test.beforeEach(async ({ umbracoApi, page }) => { + // TODO: REMOVE THIS WHEN SQLITE IS FIXED + // Wait so we don't bombard the API + await page.waitForTimeout(1000); + await umbracoApi.login(); + }); + + test.afterEach(async({umbracoApi}) => { + await umbracoApi.users.ensureEmailNotExits(email); + }); + + async function createUser(umbracoApi : ApiHelpers){ + let url = process.env.URL + "/umbraco/backoffice/umbracoapi/users/PostCreateUser"; + await umbracoApi.post(url, userData); + } + + test('Create user', async ({umbracoUi, umbracoApi, page}) => { + const name = "Alice Bobson"; + const email = "alice-bobson@acceptancetest.umbraco"; + + await umbracoApi.users.ensureEmailNotExits(email); + await umbracoUi.goToSection('users'); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey('user_createUser')); + + + await page.locator('input[name="name"]').fill(name); + await page.locator('input[name="email"]').fill(email); + + await page.locator('.umb-node-preview-add').click(); + await page.locator('.umb-user-group-picker-list-item:nth-child(1) > .umb-user-group-picker__action').click(); + await page.locator('.umb-user-group-picker-list-item:nth-child(2) > .umb-user-group-picker__action').click(); + await page.locator('.btn-success').click(); + + await page.locator('.umb-button > .btn > .umb-button__content').click(); + + // Assert + await expect(await umbracoUi.getButtonByLabelKey("user_goToProfile")).toBeVisible(); + }); + + test('Update user', async ({umbracoUi, umbracoApi, page}) => { + // Ensure user doesn't exist + await umbracoApi.users.ensureEmailNotExits(email); + + //Create user through API + await createUser(umbracoApi); + + // Go to the user and edit their name + await umbracoUi.goToSection('users'); + await page.locator(`.umb-user-card__name >> text=${name}`).click(); + await page.locator('#headerName').type('{movetoend}son'); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.save)); + + // Assert save succeeds + await umbracoUi.isSuccessNotificationVisible(); + }); + + test('Delete user', async ({umbracoUi, umbracoApi, page}) => { + + // Ensure user doesn't exist + await umbracoApi.users.ensureEmailNotExits(email); + + // Create user through API + await createUser(umbracoApi); + + // Go to the user and delete them + await umbracoUi.goToSection('users'); + await page.locator(`.umb-user-card__name >> text=${name}`).click(); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey('user_deleteUser')); + await page.locator('umb-button[label="Yes, delete"]').click(); + + // Assert deletion succeeds + await umbracoUi.isSuccessNotificationVisible(); + }); +}); \ No newline at end of file diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/appsettings.json b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/appsettings.json new file mode 100644 index 0000000000..9e26dfeeb6 --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/appsettings.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultUILanguage/appsettings.json b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultUILanguage/appsettings.json new file mode 100644 index 0000000000..d0de42cebf --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultUILanguage/appsettings.json @@ -0,0 +1,9 @@ +{ + "Umbraco": { + "CMS": { + "Global": { + "DefaultUILanguage": "da-DK" + } + } + } +} \ No newline at end of file diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultUILanguage/editDefaultUILanguage.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultUILanguage/editDefaultUILanguage.spec.ts new file mode 100644 index 0000000000..8ef1e0c0b7 --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultUILanguage/editDefaultUILanguage.spec.ts @@ -0,0 +1,36 @@ +import {ConstantHelper, test} from '@umbraco/playwright-testhelpers'; +import {expect} from "@playwright/test"; +import {UserBuilder} from '@umbraco/playwright-models'; + +test.describe('DefaultUILanguage', () => { + + test.beforeEach(async ({page, umbracoApi}) => { + await umbracoApi.login(); + }); + + test('DefaultUiLanguage da-DK', async ({page, umbracoApi, umbracoUi}) => { + const name = "test"; + const email = "Test@email.com"; + const userGroup = ["editor"]; + const language = "da-DK"; + + await umbracoApi.users.ensureEmailNotExits(email); + + const user = new UserBuilder() + .withName(name) + .withEmail(email) + .withUserGroup(userGroup) + .build() + await umbracoApi.users.postCreateUser(user); + + await page.locator('[data-element="section-users"]').click(); + await page.locator('.umb-user-card__content', {hasText: name}).click(); + + // Assert + await expect(await page.locator('[value="string:' + language + '"][selected="selected"]')).toHaveCount(1); + + // Clean + await umbracoApi.users.ensureEmailNotExits(email); + }); +}); + diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/EnableTours=False/appsettings.json b/tests/Umbraco.Tests.AcceptanceTest/tests/EnableTours=False/appsettings.json new file mode 100644 index 0000000000..7a2afd43d4 --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/EnableTours=False/appsettings.json @@ -0,0 +1,9 @@ +{ + "Umbraco": { + "CMS": { + "Tours": { + "EnableTours": false + } + } + } +} \ No newline at end of file diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/EnableTours=False/toursTest.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/EnableTours=False/toursTest.spec.ts new file mode 100644 index 0000000000..ae5c09bf0b --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/EnableTours=False/toursTest.spec.ts @@ -0,0 +1,19 @@ +import {test} from '@umbraco/playwright-testhelpers'; +import {expect} from "@playwright/test"; +import {umbracoConfig} from "../../umbraco.config"; + +test.describe('Test', () => { + + test.beforeEach(async ({page, umbracoApi}) => { + await umbracoApi.login(true); + }); + + test('Check if tours exist', async ({page, umbracoApi, umbracoUi}) => { + // Need to go to the correct page when I set skipCheckTours in login to true + await page.goto(umbracoConfig.environment.baseUrl + '/umbraco'); + await page.locator('[data-element="global-help"]').click(); + // Assert + await expect(await page.locator('[data-element="help-tours"]')).not.toBeVisible(); + }); + +}); \ No newline at end of file diff --git a/tests/Umbraco.Tests.AcceptanceTest/tsconfig.json b/tests/Umbraco.Tests.AcceptanceTest/tsconfig.json deleted file mode 100644 index 96178bfc54..0000000000 --- a/tests/Umbraco.Tests.AcceptanceTest/tsconfig.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "compileOnSave": false, - "compilerOptions": { - "baseUrl": "./", - "outDir": "./lib", - "sourceMap": false, - "declaration": true, - "module": "CommonJS", - "moduleResolution": "node", - "emitDecoratorMetadata": true, - "experimentalDecorators": true, - "esModuleInterop": true, - "importHelpers": true, - "target": "es5", - - "types": [ - "cypress", - "cy-verify-downloads" - ], - "lib": [ - "es5", - "dom" - ], - "plugins": [ - { - "name": "typescript-tslint-plugin", - "alwaysShowRuleFailuresAsWarnings": false, - "ignoreDefinitionFiles": true, - "configFile": "tslint.json", - "suppressWhileTypeErrorsPresent": false - } - ] - }, - "include": [ - "src/**/*.ts" - ] -} diff --git a/tests/Umbraco.Tests.AcceptanceTest/tslint.json b/tests/Umbraco.Tests.AcceptanceTest/tslint.json deleted file mode 100644 index a8571f3cda..0000000000 --- a/tests/Umbraco.Tests.AcceptanceTest/tslint.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": ["tslint:recommended", "tslint-config-prettier"] -}