diff --git a/e2e_tests/integration/guide-command.spec.ts b/e2e_tests/integration/guide-command.spec.ts new file mode 100644 index 00000000000..5a8e48c85e6 --- /dev/null +++ b/e2e_tests/integration/guide-command.spec.ts @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2002-2021 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* global Cypress, cy, before */ + +describe('Guide command', () => { + before(function() { + cy.visit(Cypress.config('url')) + .title() + .should('include', 'Neo4j Browser') + cy.wait(3000) + }) + + it('handles not found guides', () => { + cy.executeCommand(':clear') + cy.executeCommand(':guide not-found-guide-anywhere') + + cy.get('[data-testid="guideDrawer"]').should('contain', 'Not found') + + // reset state + cy.executeCommand(':guide') + cy.get('[data-testid="guideDrawer"]').should('contain', ':guide movie') + cy.get('[data-testid=drawerGuides]').click() + }) + + it('can walk through a guides', () => { + cy.executeCommand(':clear') + // Open a guide from the sidebar + cy.get('[data-testid=drawerGuides]').click() + cy.get('[data-testid="guideDrawer"]') + .contains(':guide cypher') + .click() + + // can progress slide + cy.get('[data-testid=guideNextSlide]').click() + cy.get('[data-testid="guideDrawer"]').contains('CREATE') + + // remembers slide location + cy.get('[data-testid=drawerGuides]').click() + cy.get('[data-testid=drawerGuides]').click() + + // can go back + cy.get('[data-testid="guideDrawer"]').contains('CREATE') + cy.get('[data-testid=guidePreviousSlide]').click() + cy.get('[data-testid="guideDrawer"]').contains('SQL-like clauses') + + // go to end + cy.get('[data-testid=guideNextSlide]').click() + cy.get('[data-testid=guideNextSlide]').click() + cy.get('[data-testid=guideNextSlide]').click() + cy.get('[data-testid=guideNextSlide]').click() + cy.get('[data-testid=guideNextSlide]').click() + cy.get('[data-testid=guideNextSlide]').click() + cy.get('[data-testid=guideNextSlide]').click() + cy.get('[data-testid=guideNextSlide]').click() + cy.get('[data-testid="guideDrawer"]').contains('Next steps') + + // switch guide via command + cy.executeCommand(':guide northwind') + cy.get('[data-testid="guideDrawer"]').contains('From RDBMS to Graph') + + // Jump to end, then new guide + cy.get('[data-testid="pagination-7"]').click() + cy.get('[data-testid="guideDrawer"]') + .contains('Movie Graph') + .click() + + cy.get('[data-testid="guideDrawer"]').contains('mini graph application') + + // Can use back button + cy.get('[data-testid="guidesBackButton"]').click() + cy.get('[data-testid="guideDrawer"]').contains(':guide cypher') + + cy.get('[data-testid=drawerGuides]').click() + }) +}) diff --git a/src/browser/components/Directives.tsx b/src/browser/components/Directives.tsx index d5f321919a2..f01f3fab4ad 100644 --- a/src/browser/components/Directives.tsx +++ b/src/browser/components/Directives.tsx @@ -31,7 +31,8 @@ const directives = [ { selector: '[data-exec]', valueExtractor: (elem: any) => { - return `${elem.getAttribute('data-exec')}` + // we prepend the : to only autoexec browser commands and not cypher + return `:${elem.getAttribute('data-exec')}` }, autoExec: true }, @@ -86,8 +87,8 @@ const directives = [ } ] -const prependPlayIcon = (element: any) => { - prependIcon(element, 'fa fa-play-circle-o') +const prependPlayIcon = (element: any, onClick: () => void) => { + prependIcon(element, 'fa fa-play-circle-o', onClick) } const bindDynamicInputToTheDom = (element: any) => { @@ -116,17 +117,22 @@ export const Directives = (props: any) => { const callback = (elem: HTMLDivElement | null) => { if (elem) { directives.forEach(directive => { - const elems = elem.querySelectorAll(directive.selector) + const elems = elem.querySelectorAll(directive.selector) Array.from(elems).forEach(e => { if ( e.firstChild?.nodeName !== 'I' && !e.classList.contains('remove-play-icon') ) { - prependPlayIcon(e) + prependPlayIcon(e, () => { + addClass(e, 'clicked') + props.onItemClick( + directive.valueExtractor(e), + true, + props.originFrameId + ) + }) } - // If we use add event listener we need to remove it afterwards - // @ts-expect-error e.onclick = () => { addClass(e, 'clicked') return props.onItemClick( @@ -147,7 +153,8 @@ const mapDispatchToProps = (_dispatch: any, ownProps: any) => { return { onItemClick: (cmd: string, autoExec: boolean, id: string) => { if (!cmd.endsWith(' null') && !cmd.endsWith(':null')) { - if (autoExec) { + // prevent autorunning cypher by prefixing w :auto hack + if (autoExec && !cmd.startsWith(':auto')) { const action = executeCommand(cmd, { id, source: commandSources.button diff --git a/src/browser/documentation/sidebar-guides/concepts.tsx b/src/browser/documentation/sidebar-guides/concepts.tsx index 556d6f1853c..cdf6aeb5024 100644 --- a/src/browser/documentation/sidebar-guides/concepts.tsx +++ b/src/browser/documentation/sidebar-guides/concepts.tsx @@ -144,10 +144,10 @@ const slides = [

Keep getting started