├── .eslintignore ├── .eslintrc.js ├── .github ├── ISSUE_TEMPLATE.md ├── ISSUE_TEMPLATE │ ├── bug.md │ ├── feature.md │ ├── question.md │ └── regression.md ├── PULL_REQUEST_TEMPLATE.md ├── stale.yml └── workflows │ ├── lock.yml │ ├── release-ide-binaries.yml │ ├── release-npm.yml │ └── test.yml ├── .gitignore ├── .npmrc ├── .prettierrc ├── .stylelintrc.js ├── .vscode └── selenium-ide.code-workspace ├── BUILD.bazel ├── CONTRIBUTING.md ├── LICENSE ├── NOTICE ├── README.md ├── WORKSPACE ├── babel.config.js ├── docker-compose.yml ├── docs └── plugins.md ├── dump.rdb ├── jest.config.js ├── package.json ├── packages ├── BUILD.bazel ├── browser-info │ ├── BUILD.bazel │ ├── jest-reporter.js │ ├── jest.config.js │ ├── package.json │ ├── src │ │ ├── __mocks__ │ │ │ └── sh.ts │ │ ├── __tests__ │ │ │ ├── chrome.spec.ts │ │ │ ├── index.spec.ts │ │ │ └── sh.spec.ts │ │ ├── chrome.ts │ │ ├── index.ts │ │ └── sh.ts │ └── tsconfig.json ├── code-export-csharp-commons │ ├── README.md │ ├── __test__ │ │ └── src │ │ │ ├── __snapshots__ │ │ │ ├── command.spec.js.snap │ │ │ └── location.spec.js.snap │ │ │ ├── command.spec.js │ │ │ ├── location.spec.js │ │ │ └── selection.spec.js │ ├── package.json │ ├── src │ │ ├── command.ts │ │ ├── index.ts │ │ ├── location.ts │ │ └── selection.ts │ └── tsconfig.json ├── code-export-csharp-nunit │ ├── README.md │ ├── __test__ │ │ └── src │ │ │ ├── __snapshots__ │ │ │ ├── command.spec.js.snap │ │ │ └── index.spec.js.snap │ │ │ ├── command.spec.js │ │ │ └── index.spec.js │ ├── package.json │ ├── src │ │ ├── command.ts │ │ ├── hook.ts │ │ └── index.ts │ └── tsconfig.json ├── code-export-csharp-xunit │ ├── README.md │ ├── __test__ │ │ └── src │ │ │ ├── __snapshots__ │ │ │ ├── command.spec.js.snap │ │ │ └── index.spec.js.snap │ │ │ ├── command.spec.js │ │ │ └── index.spec.js │ ├── package.json │ ├── src │ │ ├── command.ts │ │ ├── hook.ts │ │ └── index.ts │ └── tsconfig.json ├── code-export-java-junit │ ├── README.md │ ├── __test__ │ │ └── src │ │ │ ├── __snapshots__ │ │ │ ├── command.spec.js.snap │ │ │ ├── index.spec.js.snap │ │ │ └── location.spec.js.snap │ │ │ ├── command.spec.js │ │ │ ├── index.spec.js │ │ │ ├── location.spec.js │ │ │ └── selection.spec.js │ ├── package.json │ ├── src │ │ ├── command.ts │ │ ├── hook.ts │ │ ├── index.ts │ │ ├── location.ts │ │ └── selection.ts │ └── tsconfig.json ├── code-export-javascript-mocha │ ├── README.md │ ├── __test__ │ │ └── src │ │ │ ├── __snapshots__ │ │ │ ├── command.spec.js.snap │ │ │ ├── index.spec.js.snap │ │ │ └── location.spec.js.snap │ │ │ ├── command.spec.js │ │ │ ├── index.spec.js │ │ │ ├── location.spec.js │ │ │ └── selection.spec.js │ ├── package.json │ ├── projects │ │ └── form-select-checkbox.side │ ├── src │ │ ├── command.ts │ │ ├── hook.ts │ │ ├── index.ts │ │ ├── location.ts │ │ └── selection.ts │ ├── tests │ │ └── formSelectCheckbox.spec.js │ └── tsconfig.json ├── code-export-python-pytest │ ├── README.md │ ├── __test__ │ │ └── src │ │ │ ├── __snapshots__ │ │ │ ├── command.spec.js.snap │ │ │ ├── index.spec.js.snap │ │ │ └── location.spec.js.snap │ │ │ ├── command.spec.js │ │ │ ├── index.spec.js │ │ │ ├── location.spec.js │ │ │ └── selection.spec.js │ ├── package.json │ ├── src │ │ ├── command.ts │ │ ├── hook.ts │ │ ├── index.ts │ │ ├── location.ts │ │ └── selection.ts │ └── tsconfig.json ├── code-export-ruby-rspec │ ├── README.md │ ├── __test__ │ │ └── src │ │ │ ├── __snapshots__ │ │ │ ├── command.spec.js.snap │ │ │ ├── index.spec.js.snap │ │ │ ├── location.spec.js.snap │ │ │ └── selection.spec.js.snap │ │ │ ├── command.spec.js │ │ │ ├── index.spec.js │ │ │ ├── location.spec.js │ │ │ └── selection.spec.js │ ├── package.json │ ├── src │ │ ├── command.ts │ │ ├── hook.ts │ │ ├── index.ts │ │ ├── location.ts │ │ └── selection.ts │ └── tsconfig.json ├── get-driver │ ├── BUILD.bazel │ ├── jest.config.js │ ├── package.json │ ├── src │ │ ├── __tests__ │ │ │ ├── download-driver.spec.ts │ │ │ ├── index.spec.ts │ │ │ └── resolve-driver.spec.ts │ │ ├── download-driver.ts │ │ ├── index.ts │ │ ├── resolve-driver.ts │ │ └── types.ts │ └── tsconfig.json ├── selenium-ide │ ├── .eslintrc.js │ ├── .vscode │ │ ├── launch.json │ │ └── tasks.json │ ├── README.md │ ├── bin.js │ ├── package.json │ ├── resources │ │ ├── background.png │ │ ├── background@2x.png │ │ ├── icon.icns │ │ ├── icon.ico │ │ └── icon.png │ ├── scripts │ │ └── ide-runner.js │ ├── src │ │ ├── browser │ │ │ ├── I18N │ │ │ │ ├── en │ │ │ │ │ └── index.ts │ │ │ │ ├── keys.ts │ │ │ │ ├── util.ts │ │ │ │ └── zh │ │ │ │ │ └── index.ts │ │ │ ├── api │ │ │ │ ├── bidi.ts │ │ │ │ ├── classes │ │ │ │ │ ├── DriverEventListener.ts │ │ │ │ │ ├── DriverHandler.ts │ │ │ │ │ ├── DriverUtils.ts │ │ │ │ │ ├── EventListener.ts │ │ │ │ │ └── Handler.ts │ │ │ │ ├── index.ts │ │ │ │ └── mutator.ts │ │ │ ├── components │ │ │ │ ├── AppBar │ │ │ │ │ ├── AppBarCore.tsx │ │ │ │ │ ├── AppBarTabs.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── AppPanelWrapper.tsx │ │ │ │ ├── AppWrapper.tsx │ │ │ │ ├── Controls │ │ │ │ │ ├── BaseProps.tsx │ │ │ │ │ ├── PauseButton.tsx │ │ │ │ │ ├── PlayButton.tsx │ │ │ │ │ ├── PlayListButton.tsx │ │ │ │ │ ├── PlayNextStepButton.tsx │ │ │ │ │ ├── RecordButton.tsx │ │ │ │ │ └── StopButton.tsx │ │ │ │ ├── DraggableListItem.tsx │ │ │ │ ├── Drawer │ │ │ │ │ ├── EditorToolbar.tsx │ │ │ │ │ ├── Header.tsx │ │ │ │ │ ├── RenamableListItem.tsx │ │ │ │ │ ├── Wrapper.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── DropTargetListItem.tsx │ │ │ │ ├── Input │ │ │ │ │ └── index.tsx │ │ │ │ ├── Logger │ │ │ │ │ └── index.tsx │ │ │ │ ├── Main │ │ │ │ │ ├── Header.tsx │ │ │ │ │ ├── MainCore.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── PanelNav.tsx │ │ │ │ ├── PlaybackControls │ │ │ │ │ └── index.tsx │ │ │ │ ├── PlaybackDimensionControls │ │ │ │ │ └── index.tsx │ │ │ │ ├── PlaybackPanel │ │ │ │ │ └── index.tsx │ │ │ │ ├── PlaybackTabBar │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── tab.tsx │ │ │ │ ├── ProjectEditor │ │ │ │ │ └── index.tsx │ │ │ │ ├── ReorderableList.tsx │ │ │ │ ├── ReorderableListItem.tsx │ │ │ │ ├── ResizeHandle │ │ │ │ │ └── index.tsx │ │ │ │ ├── Tab │ │ │ │ │ ├── Panel.tsx │ │ │ │ │ └── PanelMulti.tsx │ │ │ │ ├── URLBar │ │ │ │ │ └── index.tsx │ │ │ │ ├── UncontrolledTextField.tsx │ │ │ │ └── types.ts │ │ │ ├── contexts │ │ │ │ ├── active-command.ts │ │ │ │ ├── active-test.ts │ │ │ │ ├── command-map.ts │ │ │ │ ├── config-settings-group.ts │ │ │ │ ├── config.ts │ │ │ │ ├── playback-command-states.ts │ │ │ │ ├── playback-current-index.ts │ │ │ │ ├── playback-test-results.ts │ │ │ │ ├── provider.tsx │ │ │ │ ├── session.ts │ │ │ │ ├── show-drawer.ts │ │ │ │ ├── status.ts │ │ │ │ ├── suite-mode.ts │ │ │ │ ├── suites.ts │ │ │ │ └── tests.ts │ │ │ ├── enums │ │ │ │ └── tab.ts │ │ │ ├── helpers │ │ │ │ ├── getEntryColor.ts │ │ │ │ ├── preload-bidi.ts │ │ │ │ ├── preload-electron.ts │ │ │ │ ├── preload.ts │ │ │ │ ├── renderWhenReady.ts │ │ │ │ ├── string.ts │ │ │ │ ├── subscribeToSession.ts │ │ │ │ └── useHeightFromElement.ts │ │ │ ├── highlight.css │ │ │ ├── hooks │ │ │ │ ├── useKeyboundNav.ts │ │ │ │ ├── useMouseExit.ts │ │ │ │ ├── usePanelGroup.ts │ │ │ │ └── useReorderPreview.ts │ │ │ ├── index.css │ │ │ ├── types.ts │ │ │ └── windows │ │ │ │ ├── Alert │ │ │ │ ├── controller.ts │ │ │ │ ├── preload.ts │ │ │ │ └── renderer.tsx │ │ │ │ ├── Confirm │ │ │ │ ├── controller.ts │ │ │ │ ├── preload.ts │ │ │ │ └── renderer.tsx │ │ │ │ ├── PlaybackWindow │ │ │ │ ├── controller.ts │ │ │ │ ├── preload.ts │ │ │ │ ├── preload │ │ │ │ │ ├── __mocks__ │ │ │ │ │ │ └── utils.js │ │ │ │ │ ├── find-select.ts │ │ │ │ │ ├── locator-builders.ts │ │ │ │ │ ├── prompt-injector.ts │ │ │ │ │ ├── prompt.ts │ │ │ │ │ ├── record-handlers.ts │ │ │ │ │ ├── record-shortcuts.ts │ │ │ │ │ ├── recorder.ts │ │ │ │ │ ├── target-selector.ts │ │ │ │ │ ├── third-party │ │ │ │ │ │ └── find-element.js │ │ │ │ │ └── utils.ts │ │ │ │ └── renderer.tsx │ │ │ │ ├── PlaybackWindowBidi │ │ │ │ ├── controller.ts │ │ │ │ ├── preload.ts │ │ │ │ ├── preload │ │ │ │ │ ├── record-shortcuts.ts │ │ │ │ │ └── recorder.ts │ │ │ │ └── renderer.tsx │ │ │ │ ├── ProjectEditor │ │ │ │ ├── controller.ts │ │ │ │ ├── preload.ts │ │ │ │ ├── renderer.tsx │ │ │ │ └── tabs │ │ │ │ │ ├── Project │ │ │ │ │ ├── DriverSelect.tsx │ │ │ │ │ ├── Message.tsx │ │ │ │ │ ├── OutPutSettings.tsx │ │ │ │ │ ├── ProjectDrawer.tsx │ │ │ │ │ ├── ProjectSettings.tsx │ │ │ │ │ ├── ProjectTab.tsx │ │ │ │ │ ├── SettingTabs.tsx │ │ │ │ │ └── SystemSettings.tsx │ │ │ │ │ ├── Suites │ │ │ │ │ ├── Controls.tsx │ │ │ │ │ ├── Editor │ │ │ │ │ │ ├── AvailableSuiteTestList.tsx │ │ │ │ │ │ ├── AvailableSuiteTestRow.tsx │ │ │ │ │ │ ├── CurrentSuiteTestList.tsx │ │ │ │ │ │ ├── CurrentSuiteTestRow.tsx │ │ │ │ │ │ ├── SuiteEditor.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── SuiteCreateDialog.tsx │ │ │ │ │ ├── SuiteDeleteDialog.tsx │ │ │ │ │ ├── SuiteSelector.tsx │ │ │ │ │ ├── SuitesDrawer.tsx │ │ │ │ │ ├── SuitesTab.tsx │ │ │ │ │ ├── Toolbar.tsx │ │ │ │ │ └── Viewer │ │ │ │ │ │ ├── Entry.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ └── Tests │ │ │ │ │ ├── CommandFields │ │ │ │ │ ├── ArgField.tsx │ │ │ │ │ ├── CommandSelector.tsx │ │ │ │ │ ├── LocatorField.tsx │ │ │ │ │ ├── TextField.tsx │ │ │ │ │ └── utils.ts │ │ │ │ │ ├── Controls.tsx │ │ │ │ │ ├── TestCommandEditor.tsx │ │ │ │ │ ├── TestCommandList.tsx │ │ │ │ │ ├── TestCommandListItem.tsx │ │ │ │ │ ├── TestCommandOverlay.tsx │ │ │ │ │ ├── TestCommandRow.tsx │ │ │ │ │ ├── TestCommandTable.tsx │ │ │ │ │ ├── TestCreateDialog.tsx │ │ │ │ │ ├── TestDeleteDialog.tsx │ │ │ │ │ ├── TestRenameDialog.tsx │ │ │ │ │ ├── TestSelector.tsx │ │ │ │ │ ├── TestsDrawer.tsx │ │ │ │ │ ├── TestsTab.tsx │ │ │ │ │ └── types.ts │ │ │ │ ├── Prompt │ │ │ │ ├── controller.ts │ │ │ │ ├── preload.ts │ │ │ │ └── renderer.tsx │ │ │ │ ├── Splash │ │ │ │ ├── controller.ts │ │ │ │ ├── preload.ts │ │ │ │ └── renderer.tsx │ │ │ │ ├── UpdateNotifier │ │ │ │ ├── controller.ts │ │ │ │ ├── preload.ts │ │ │ │ └── renderer.tsx │ │ │ │ └── controller.ts │ │ └── main │ │ │ ├── api │ │ │ ├── classes │ │ │ │ ├── DriverEventListener.ts │ │ │ │ ├── DriverHandler.ts │ │ │ │ ├── DriverRawHandler.ts │ │ │ │ ├── EventListener.ts │ │ │ │ ├── Handler.ts │ │ │ │ └── RawHandler.ts │ │ │ ├── helpers │ │ │ │ └── getCore.ts │ │ │ └── index.ts │ │ │ ├── constants │ │ │ ├── preloadScriptPath.ts │ │ │ └── rendererPath.ts │ │ │ ├── index.ts │ │ │ ├── install-react-devtools.ts │ │ │ ├── log.ts │ │ │ ├── session │ │ │ ├── controllers │ │ │ │ ├── ArgTypes │ │ │ │ │ └── index.ts │ │ │ │ ├── Base │ │ │ │ │ └── index.ts │ │ │ │ ├── Channels │ │ │ │ │ └── index.ts │ │ │ │ ├── Commands │ │ │ │ │ └── index.ts │ │ │ │ ├── Dialogs │ │ │ │ │ └── index.ts │ │ │ │ ├── Driver │ │ │ │ │ ├── bidi.ts │ │ │ │ │ ├── download.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── start.ts │ │ │ │ ├── Menu │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── menus │ │ │ │ │ │ ├── application.ts │ │ │ │ │ │ ├── editBasics.ts │ │ │ │ │ │ ├── help.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── playback.ts │ │ │ │ │ │ ├── projectEditor.ts │ │ │ │ │ │ ├── projectView.ts │ │ │ │ │ │ ├── suiteManager.ts │ │ │ │ │ │ ├── testEditor.ts │ │ │ │ │ │ ├── testManager.ts │ │ │ │ │ │ └── textField.ts │ │ │ │ │ └── utils.ts │ │ │ │ ├── OutputFormats │ │ │ │ │ └── index.tsx │ │ │ │ ├── Playback │ │ │ │ │ └── index.ts │ │ │ │ ├── Plugins │ │ │ │ │ └── index.ts │ │ │ │ ├── Polyfill │ │ │ │ │ └── index.ts │ │ │ │ ├── Projects │ │ │ │ │ ├── Recent.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── Recorder │ │ │ │ │ └── index.ts │ │ │ │ ├── ResizablePanels │ │ │ │ │ └── index.ts │ │ │ │ ├── State │ │ │ │ │ └── index.ts │ │ │ │ ├── Suites │ │ │ │ │ └── index.ts │ │ │ │ ├── System │ │ │ │ │ └── index.ts │ │ │ │ ├── Tests │ │ │ │ │ └── index.ts │ │ │ │ └── Windows │ │ │ │ │ └── index.ts │ │ │ └── index.ts │ │ │ ├── store │ │ │ ├── config.ts │ │ │ └── index.ts │ │ │ ├── types.ts │ │ │ └── util.ts │ ├── tsconfig.json │ └── webpack.config.ts ├── side-api │ ├── package.json │ ├── src │ │ ├── classes │ │ │ ├── Constant.ts │ │ │ └── ProxyTrap.ts │ │ ├── commands │ │ │ ├── channels │ │ │ │ ├── index.ts │ │ │ │ ├── onSend.ts │ │ │ │ └── send.ts │ │ │ ├── dialogs │ │ │ │ ├── confirm.ts │ │ │ │ ├── index.ts │ │ │ │ └── open.ts │ │ │ ├── driver │ │ │ │ ├── download.ts │ │ │ │ ├── index.ts │ │ │ │ ├── listBrowsers.ts │ │ │ │ ├── selectBrowser.ts │ │ │ │ ├── startProcess.ts │ │ │ │ ├── stopProcess.ts │ │ │ │ └── takeScreenshot.ts │ │ │ ├── menus │ │ │ │ ├── index.ts │ │ │ │ ├── open.ts │ │ │ │ └── openSync.ts │ │ │ ├── playback │ │ │ │ ├── index.ts │ │ │ │ ├── onAfter.ts │ │ │ │ ├── onAfterAll.ts │ │ │ │ ├── onBefore.ts │ │ │ │ ├── onBeforeAll.ts │ │ │ │ ├── onPlayUpdate.ts │ │ │ │ ├── onStepUpdate.ts │ │ │ │ ├── pause.ts │ │ │ │ ├── performCommand.ts │ │ │ │ ├── play.ts │ │ │ │ ├── playSuite.ts │ │ │ │ ├── resume.ts │ │ │ │ └── stop.ts │ │ │ ├── plugins │ │ │ │ ├── addPreloadScript.ts │ │ │ │ ├── addRecorderPreprocessor.ts │ │ │ │ ├── getPreloads.ts │ │ │ │ ├── index.ts │ │ │ │ ├── list.ts │ │ │ │ ├── listPreloadPaths.ts │ │ │ │ ├── onRequestCustomEditorPanel.ts │ │ │ │ ├── projectCreate.ts │ │ │ │ ├── projectDelete.ts │ │ │ │ └── projectEdit.ts │ │ │ ├── projects │ │ │ │ ├── getActive.ts │ │ │ │ ├── getRecent.ts │ │ │ │ ├── index.ts │ │ │ │ ├── load.ts │ │ │ │ ├── new.ts │ │ │ │ ├── save.ts │ │ │ │ ├── select.ts │ │ │ │ └── update.ts │ │ │ ├── recorder │ │ │ │ ├── getFrameLocation.ts │ │ │ │ ├── getLocatorOrder.ts │ │ │ │ ├── getWinHandleId.ts │ │ │ │ ├── index.ts │ │ │ │ ├── onFrameDeleted.ts │ │ │ │ ├── onFrameRecalculate.ts │ │ │ │ ├── onHighlightElement.ts │ │ │ │ ├── onLocatorOrderChanged.ts │ │ │ │ ├── onNewWindow.ts │ │ │ │ ├── onRequestElementAt.ts │ │ │ │ ├── onRequestSelectElement.ts │ │ │ │ ├── recordNewCommand.ts │ │ │ │ ├── requestElementAt.ts │ │ │ │ ├── requestHighlightElement.ts │ │ │ │ ├── requestSelectElement.ts │ │ │ │ ├── selectElement.ts │ │ │ │ ├── setWindowHandle.ts │ │ │ │ ├── start.ts │ │ │ │ └── stop.ts │ │ │ ├── resizable-panels │ │ │ │ ├── get-panel-group.ts │ │ │ │ ├── index.ts │ │ │ │ └── set-panel-group.ts │ │ │ ├── state │ │ │ │ ├── closeTestEditor.ts │ │ │ │ ├── get.ts │ │ │ │ ├── getUserPrefs.ts │ │ │ │ ├── index.ts │ │ │ │ ├── onMutate.ts │ │ │ │ ├── openTestEditor.ts │ │ │ │ ├── redo.ts │ │ │ │ ├── set.ts │ │ │ │ ├── setActiveCommand.ts │ │ │ │ ├── setActiveSuite.ts │ │ │ │ ├── setActiveTest.ts │ │ │ │ ├── setAll.ts │ │ │ │ ├── setCopiedCommands.ts │ │ │ │ ├── toggleBreakpoint.ts │ │ │ │ ├── toggleSuiteMode.ts │ │ │ │ ├── toggleUserPrefCamelCase.ts │ │ │ │ ├── toggleUserPrefDisableCodeExportCompat.ts │ │ │ │ ├── toggleUserPrefIgnoreCertificateErrors.ts │ │ │ │ ├── toggleUserPrefInsert.ts │ │ │ │ ├── toggleUserPrefTheme.ts │ │ │ │ ├── undo.ts │ │ │ │ ├── updateStepSelection.ts │ │ │ │ └── updateTestSelection.ts │ │ │ ├── suites │ │ │ │ ├── addTests.ts │ │ │ │ ├── create.ts │ │ │ │ ├── delete.ts │ │ │ │ ├── index.ts │ │ │ │ ├── removeTests.ts │ │ │ │ ├── reorderTests.ts │ │ │ │ └── update.ts │ │ │ ├── system │ │ │ │ ├── crash.ts │ │ │ │ ├── getLanguage.ts │ │ │ │ ├── getLanguageMap.ts │ │ │ │ ├── getLogPath.ts │ │ │ │ ├── index.ts │ │ │ │ ├── onLog.ts │ │ │ │ ├── quit.ts │ │ │ │ └── setLanguage.ts │ │ │ ├── tests │ │ │ │ ├── addSteps.ts │ │ │ │ ├── create.ts │ │ │ │ ├── delete.ts │ │ │ │ ├── index.ts │ │ │ │ ├── removeSteps.ts │ │ │ │ ├── rename.ts │ │ │ │ ├── reorderSteps.ts │ │ │ │ ├── toggleStepDisability.ts │ │ │ │ └── updateStep.ts │ │ │ └── windows │ │ │ │ ├── close.ts │ │ │ │ ├── closePlaybackWindow.ts │ │ │ │ ├── focusPlaybackWindow.ts │ │ │ │ ├── index.ts │ │ │ │ ├── navigatePlaybackWindow.ts │ │ │ │ ├── onPlaybackWindowChanged.ts │ │ │ │ ├── onPlaybackWindowClosed.ts │ │ │ │ ├── onPlaybackWindowOpened.ts │ │ │ │ ├── open.ts │ │ │ │ ├── openCustom.ts │ │ │ │ ├── requestCustomEditorPanel.ts │ │ │ │ ├── requestPlaybackWindow.ts │ │ │ │ └── shiftFocus.ts │ │ ├── constants │ │ │ ├── badIndex.ts │ │ │ ├── index.ts │ │ │ └── loadingID.ts │ │ ├── helpers │ │ │ ├── getActiveData.ts │ │ │ ├── hasID.ts │ │ │ ├── index.ts │ │ │ ├── reorderList.ts │ │ │ ├── string.ts │ │ │ └── updateSelection.ts │ │ ├── index.ts │ │ ├── models │ │ │ ├── index.ts │ │ │ ├── project │ │ │ │ ├── command.ts │ │ │ │ ├── index.ts │ │ │ │ ├── project.ts │ │ │ │ ├── snapshot.ts │ │ │ │ ├── snapshotTest.ts │ │ │ │ ├── suite.ts │ │ │ │ └── test.ts │ │ │ └── state │ │ │ │ ├── command.ts │ │ │ │ └── index.ts │ │ ├── process.ts │ │ └── types │ │ │ ├── api.ts │ │ │ ├── base.ts │ │ │ ├── index.ts │ │ │ └── plugin.ts │ └── tsconfig.json ├── side-cli │ ├── .babelrc │ ├── BUILD.bazel │ ├── package.json │ ├── src │ │ ├── components │ │ │ ├── log.tsx │ │ │ ├── playback.tsx │ │ │ ├── step.tsx │ │ │ ├── test-results.tsx │ │ │ ├── test-selector.tsx │ │ │ └── test-title.tsx │ │ └── index.tsx │ └── tsconfig.json ├── side-code-export │ ├── README.md │ ├── __test__ │ │ ├── src │ │ │ ├── code-export │ │ │ │ ├── emit.spec.ts.skip │ │ │ │ ├── find.spec.js.skip │ │ │ │ ├── hook.spec.ts │ │ │ │ ├── parsers.spec.ts │ │ │ │ ├── preprocessor.spec.ts │ │ │ │ ├── prettify.spec.ts │ │ │ │ ├── register.spec.ts │ │ │ │ └── render.spec.js │ │ │ ├── project │ │ │ │ └── index.spec.js │ │ │ └── string-escape │ │ │ │ └── index.spec.js │ │ └── test-files │ │ │ ├── control-flow-suite.side │ │ │ ├── nested-select-window-v2.side │ │ │ ├── nested-select-window.side │ │ │ ├── select-window.side │ │ │ ├── single-suite.side │ │ │ ├── single-test.side │ │ │ └── test-case-reuse.side │ ├── package.json │ ├── src │ │ ├── bin.ts │ │ ├── code-export │ │ │ ├── defaults.ts │ │ │ ├── emit.ts │ │ │ ├── find.ts │ │ │ ├── hook.ts │ │ │ ├── index.ts │ │ │ ├── parsers.ts │ │ │ ├── preprocessor.ts │ │ │ ├── prettify.ts │ │ │ ├── register.ts │ │ │ ├── render.ts │ │ │ └── utils.ts │ │ ├── file-writer │ │ │ ├── emit-suite.ts │ │ │ ├── emit-test.ts │ │ │ └── index.ts │ │ ├── index.ts │ │ ├── project │ │ │ └── index.ts │ │ ├── string-escape │ │ │ └── index.ts │ │ └── types.ts │ └── tsconfig.json ├── side-commons │ ├── BUILD.bazel │ ├── package.json │ ├── src │ │ ├── async.ts │ │ ├── events.ts │ │ ├── index.ts │ │ ├── string.ts │ │ └── types.ts │ └── tsconfig.json ├── side-example-suite │ ├── README.md │ ├── package.json │ ├── projects │ │ └── plugin.side │ ├── src │ │ ├── formats │ │ │ └── custom-python.ts │ │ └── plugins │ │ │ └── custom-click │ │ │ ├── index.ts │ │ │ └── preload │ │ │ └── index.ts │ ├── tests │ │ └── test_checks.py │ └── tsconfig.json ├── side-migrate │ ├── BUILD.bazel │ ├── README.md │ ├── __tests__ │ │ ├── legacy │ │ │ ├── IDE_test.html │ │ │ ├── IDE_test_10.html │ │ │ ├── IDE_test_10 │ │ │ │ ├── Log in as test user.htm │ │ │ │ ├── Log out from BO.htm │ │ │ │ └── Suite login_multiple cases.htm │ │ │ ├── IDE_test_2.html │ │ │ ├── IDE_test_3.html │ │ │ ├── IDE_test_4 │ │ │ │ ├── 000_clear_mandant_Suite.html │ │ │ │ ├── 001_clear_mandant_Suite.html │ │ │ │ └── einzeltests │ │ │ │ │ ├── DMS_clear.html │ │ │ │ │ ├── MH_delete.html │ │ │ │ │ └── kontakte_leeren.html │ │ │ ├── IDE_test_8.html │ │ │ ├── IDE_test_9.html │ │ │ ├── IDE_test_wait.html │ │ │ └── migrate.spec.ts │ │ ├── migrate.spec.ts │ │ └── migrations │ │ │ ├── implicit-locators.spec.ts │ │ │ ├── pause.spec.ts │ │ │ ├── prompt.spec.ts │ │ │ ├── script-interpolation.spec.ts │ │ │ ├── select-window.spec.ts │ │ │ ├── store-element-count.spec.ts │ │ │ ├── target-fallback.spec.ts │ │ │ ├── title.spec.ts │ │ │ ├── variable-name.spec.ts │ │ │ └── wait-for-commands.spec.ts │ ├── jest.config.js │ ├── package.json │ ├── src │ │ ├── bin.ts │ │ ├── legacy │ │ │ └── migrate.ts │ │ ├── migrate.ts │ │ ├── migrations │ │ │ ├── implicit-locators.ts │ │ │ ├── index.ts │ │ │ ├── pause.ts │ │ │ ├── prompt.ts │ │ │ ├── script-interpolation.ts │ │ │ ├── select-window.ts │ │ │ ├── store-element-count.ts │ │ │ ├── target-fallback.ts │ │ │ ├── title.ts │ │ │ ├── variable-name.ts │ │ │ └── wait-for-commands.ts │ │ └── types.d.ts │ └── tsconfig.json ├── side-model │ ├── BUILD.bazel │ ├── __tests__ │ │ ├── args │ │ │ ├── arg-type.spec.ts │ │ │ ├── argument.spec.ts │ │ │ ├── index.spec.ts │ │ │ ├── text.spec.ts │ │ │ └── variable.spec.ts │ │ ├── commands │ │ │ ├── command.spec.ts │ │ │ └── store.spec.ts │ │ └── index.spec.ts │ ├── package.json │ ├── src │ │ ├── ArgTypes.ts │ │ ├── Commands.ts │ │ ├── I18N │ │ │ ├── en │ │ │ │ ├── ArgTypes.ts │ │ │ │ └── Commands.ts │ │ │ ├── index.ts │ │ │ └── zh │ │ │ │ ├── ArgTypes.ts │ │ │ │ └── Commands.ts │ │ ├── args │ │ │ ├── arg-type.ts │ │ │ ├── argument.ts │ │ │ ├── index.ts │ │ │ ├── text.ts │ │ │ └── variable.ts │ │ ├── commands │ │ │ ├── command.ts │ │ │ └── store.ts │ │ ├── index.ts │ │ └── types.ts │ └── tsconfig.json ├── side-runner │ ├── CHANGELOG.md │ ├── README.md │ ├── jest.config.js │ ├── package.json │ ├── runner.jest.config.js │ ├── src │ │ ├── __tests__ │ │ │ ├── capabilities.spec.ts │ │ │ ├── config.spec.ts │ │ │ ├── config_1.yml │ │ │ ├── config_2.yml │ │ │ ├── proxy.spec.ts │ │ │ └── versioner.spec.ts │ │ ├── bin.ts │ │ ├── capabilities.ts │ │ ├── config.ts │ │ ├── connect.ts │ │ ├── main.test.ts │ │ ├── proxy.ts │ │ ├── run.ts │ │ ├── types.ts │ │ └── versioner.ts │ └── tsconfig.json ├── side-runtime │ ├── BUILD.bazel │ ├── jest.config.js │ ├── package.json │ ├── src │ │ ├── __tests__ │ │ │ ├── __snapshots__ │ │ │ │ └── playback.spec.ts.snap │ │ │ ├── callstack.spec.ts │ │ │ ├── integration │ │ │ │ ├── playback-webdriver.spec.ts │ │ │ │ └── recording-syncronizer-webdriver.spec.ts.ex │ │ │ ├── playback-tree │ │ │ │ ├── command-leveler.spec.ts │ │ │ │ ├── command-node.spec.ts │ │ │ │ ├── playback-tree.spec.ts │ │ │ │ └── syntax-validation.spec.ts │ │ │ ├── playback.spec.ts │ │ │ ├── preprocessors.spec.ts │ │ │ ├── util │ │ │ │ ├── FakeExecutor.ts │ │ │ │ └── JestFakeExecutor.ts │ │ │ ├── utils.spec.ts │ │ │ └── webdriver.spec.ts │ │ ├── callstack.ts │ │ ├── errors │ │ │ ├── assertion.ts │ │ │ ├── index.ts │ │ │ └── verification.ts │ │ ├── index.ts │ │ ├── playback-tree │ │ │ ├── command-leveler.ts │ │ │ ├── command-node.ts │ │ │ ├── commands.ts │ │ │ ├── control-flow-syntax-error.ts │ │ │ ├── index.ts │ │ │ ├── state.ts │ │ │ └── syntax-validation.ts │ │ ├── playback.ts │ │ ├── plugins.ts │ │ ├── preprocessors.ts │ │ ├── recording-syncronizer-webdriver.ts │ │ ├── recording-syncronizer.ts │ │ ├── types.ts │ │ ├── utils.ts │ │ ├── variables.ts │ │ └── webdriver.ts │ └── tsconfig.json ├── side-test-metaparser │ ├── jest.config.js │ ├── package.json │ ├── src │ │ ├── index.ts │ │ ├── lang-example.ts │ │ ├── parsers │ │ │ ├── click.ts │ │ │ ├── comment.ts │ │ │ ├── execute-script.ts │ │ │ ├── find-element-locator.ts │ │ │ ├── log-string-static.ts │ │ │ ├── log-string-with-variables.ts │ │ │ ├── select-element.ts │ │ │ ├── send-keys.ts │ │ │ ├── set-nested-variable.ts │ │ │ ├── set-variable.ts │ │ │ ├── wait-for-attribute.ts │ │ │ ├── wait-for-text.ts │ │ │ └── wait-for-value.ts │ │ └── types.ts │ └── tsconfig.json ├── side-testkit │ ├── BUILD.bazel │ ├── fixtures │ │ └── static │ │ │ ├── check.html │ │ │ ├── click.html │ │ │ ├── contenteditable.html │ │ │ ├── dnd.html │ │ │ ├── editable.html │ │ │ ├── file.html │ │ │ ├── form.html │ │ │ ├── form2.html │ │ │ ├── frames-manual.html │ │ │ ├── iframe.html │ │ │ ├── mouse │ │ │ ├── down.html │ │ │ ├── index.html │ │ │ ├── move.html │ │ │ ├── out.html │ │ │ ├── over.html │ │ │ ├── overout.html │ │ │ └── updown.html │ │ │ ├── nested_frame.html │ │ │ ├── popup │ │ │ ├── alert.html │ │ │ ├── confirm.html │ │ │ └── prompt.html │ │ │ ├── scroll │ │ │ └── infinite.html │ │ │ ├── select.html │ │ │ ├── selectors │ │ │ └── children.html │ │ │ ├── store │ │ │ ├── attributes.html │ │ │ └── nodes.html │ │ │ ├── success.html │ │ │ ├── text.html │ │ │ ├── textarea.html │ │ │ ├── title.html │ │ │ ├── value.html │ │ │ ├── wait │ │ │ ├── editable.html │ │ │ ├── implicit.html │ │ │ ├── not-editable.html │ │ │ ├── not-present.html │ │ │ ├── not-visible.html │ │ │ ├── present.html │ │ │ └── visible.html │ │ │ └── windows.html │ ├── package.json │ ├── src │ │ ├── index.ts │ │ └── static.ts │ └── tsconfig.json └── webdriver-testkit │ ├── BUILD.bazel │ ├── __tests__ │ ├── driver.spec.ts │ └── index.spec.ts │ ├── package.json │ ├── scripts │ └── download-drivers.js │ ├── src │ ├── cache.ts │ ├── driver.ts │ ├── index.ts │ └── update-drivers.ts │ └── tsconfig.json ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── scripts └── jest │ └── test.config.js ├── test.config.js ├── tests └── examples │ ├── blank.side │ ├── check-examples.side │ ├── control-flow.side │ ├── echo.side │ ├── execute-script.side │ ├── executescript.side │ ├── frames.side │ ├── mouse-events.side │ ├── nginx-examples.side │ ├── plugin.side │ ├── select-examples.side │ ├── select-window.side │ ├── send-keys.side │ ├── simple-parent.side │ ├── store-examples.side │ ├── text-comparison.side │ ├── unmatched-suite.side │ ├── variable-in-conditional.side │ └── wait-for.side ├── tsconfig.base.json └── tsconfig.json /.eslintignore: -------------------------------------------------------------------------------- 1 | # No wasted processing 2 | **/node_modules 3 | **/dist 4 | **/tsconfig.tsbuildinfo 5 | **/gen 6 | **/trace 7 | **/.eslintrc.js 8 | **/code-export-* 9 | # Outside of the style guide due to require 10 | babel.config.js -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## 👉 [Please follow one of these issue templates](https://github.com/SeleniumHQ/selenium-ide/issues/new/choose) 👈 2 | 3 | Note: to keep the backlog clean and actionable, issues may be immediately closed if they do not follow one of the above issue templates. 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | name: 🚀 Feature Proposal 4 | about: Submit a proposal for a new feature 5 | --- 6 | 7 | ## 🚀 Feature Proposal 8 | 9 | A clear and concise description of what the feature is. 10 | 11 | ## Motivation 12 | 13 | Please outline the motivation for the proposal. 14 | 15 | ## Example 16 | 17 | Please provide an example for how this feature would be used. 18 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | name: 💬 Questions / Help 4 | about: If you have questions, please check our IRC or Slack 5 | --- 6 | 7 | ## 💬 Questions and Help 8 | 9 | ### Please note that this issue tracker is not a help form and this issue will be closed. 10 | 11 | For questions or help please see: 12 | 13 | - [SeleniumHQ IRC channel](https://webchat.freenode.net/) 14 | - [SeleniumHQ Slack channel](https://seleniumhq.herokuapp.com/) 15 | - The [Selenium Users](https://groups.google.com/forum/#!forum/selenium-users) google group 16 | -------------------------------------------------------------------------------- /.github/workflows/lock.yml: -------------------------------------------------------------------------------- 1 | # Configuration for Lock Threads - https://github.com/dessant/lock-threads 2 | name: 'Lock Issues' 3 | 4 | on: 5 | workflow_dispatch: 6 | schedule: 7 | - cron: '45 21 * * *' 8 | 9 | permissions: 10 | issues: write 11 | pull-requests: write 12 | 13 | jobs: 14 | action: 15 | if: github.repository_owner == 'seleniumhq' 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: dessant/lock-threads@v5 19 | with: 20 | process-only: 'issues' 21 | issue-inactive-days: '30' 22 | issue-lock-reason: '' 23 | issue-comment: > 24 | This issue has been automatically locked since there 25 | has not been any recent activity since it was closed. 26 | Please open a new issue for related bugs. 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | eb-ext-artifacts 2 | node_modules 3 | _MACOSX/ 4 | .DS_Store 5 | build 6 | .idea 7 | selenium-ide.pem 8 | selenium-ide.crx 9 | dist/ 10 | .web-extension-id 11 | *.log 12 | tests/webdriver/ 13 | tests/static/webdriver.js 14 | .*.swp 15 | website/i18n 16 | tsconfig.tsbuildinfo 17 | tsconfig.build.tsbuildinfo 18 | files 19 | drivers 20 | *.d.ts.map 21 | !packages/side-migrate/src/types.d.ts 22 | 23 | # auto-generated docs 24 | docs/api/arguments.md 25 | docs/api/commands.md 26 | 27 | # v3 remants when switching between branches 28 | packages/selenium-ide/selenium/ 29 | .peru/ 30 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | strict-peer-dependencies=false 2 | # Shared tooling 3 | public-hoist-pattern[]=@bazel* 4 | public-hoist-pattern[]=@types* 5 | public-hoist-pattern[]=*babel* 6 | public-hoist-pattern[]=*eslint* 7 | public-hoist-pattern[]=*jest* 8 | public-hoist-pattern[]=*side-runner* 9 | public-hoist-pattern[]=stylelint* 10 | public-hoist-pattern[]=typescript* 11 | public-hoist-pattern[]=electron 12 | public-hoist-pattern[]=tsc 13 | # Bad to hoist 14 | hoist-pattern[]=!electron-chromedriver -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "es5", 3 | "semi": false, 4 | "singleQuote": true, 5 | "endOfLine": "lf" 6 | } 7 | -------------------------------------------------------------------------------- /.stylelintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['stylelint-config-standard', 'stylelint-config-prettier'], 3 | plugins: ['stylelint-prettier'], 4 | rules: { 5 | 'prettier/prettier': true, 6 | }, 7 | } 8 | -------------------------------------------------------------------------------- /BUILD.bazel: -------------------------------------------------------------------------------- 1 | filegroup( 2 | name="license", 3 | srcs=[ 4 | "LICENSE", 5 | "NOTICE", 6 | ], 7 | visibility=["//visibility:public"], 8 | ) 9 | exports_files(["tsconfig.json", "jest.config.js", "babel.config.js", "test.config.js"], visibility=["//visibility:public"]) 10 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Copyright 2011-2016 Software Freedom Conservancy 2 | Copyright 2004-2011 Selenium committers 3 | Copyright 2016-2017 SideeX committers 4 | 5 | This software (SideeX 2) preserves several key features of Selenium IDE and provides a number of advanced improvements. The preserved source code is copyrighted by Software Freedom Conservancy (2011-2016) and Selenium committers (2004-2011). The extended part of source code is copyrighted by SideeX committers (2016-2017). 6 | 7 | -------------------------------------------------------------------------------- /WORKSPACE: -------------------------------------------------------------------------------- 1 | workspace( 2 | name="seleniumide", 3 | managed_directories={ 4 | # Share the node_modules directory between Bazel and other tooling 5 | "@npm": ["node_modules"], 6 | }, 7 | ) 8 | 9 | load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") 10 | 11 | http_archive( 12 | name = "build_bazel_rules_nodejs", 13 | sha256 = "5bf77cc2d13ddf9124f4c1453dd96063774d755d4fc75d922471540d1c9a8ea8", 14 | urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/2.0.0/rules_nodejs-2.0.0.tar.gz"], 15 | ) 16 | 17 | load("@build_bazel_rules_nodejs//:index.bzl", "yarn_install") 18 | 19 | yarn_install( 20 | name = "npm", 21 | package_json = "//:package.json", 22 | yarn_lock = "//:yarn.lock", 23 | ) 24 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const path = require('path') 3 | 4 | module.exports = { 5 | babelrcRoots: fs 6 | .readdirSync(path.join(__dirname, 'packages')) 7 | .map((dirname) => path.join(__dirname, 'packages', dirname)) 8 | .filter((dirpath) => { 9 | const stats = fs.statSync(dirpath) 10 | return stats.isDirectory() 11 | }), 12 | overrides: [ 13 | { 14 | presets: ['@babel/preset-typescript'], 15 | test: /\.tsx?$/, 16 | }, 17 | ], 18 | presets: [ 19 | [ 20 | '@babel/preset-env', 21 | { 22 | targets: { 23 | node: '14', 24 | }, 25 | }, 26 | ], 27 | ], 28 | plugins: [ 29 | '@babel/plugin-proposal-export-namespace-from', 30 | '@babel/plugin-proposal-class-properties', 31 | ], 32 | } 33 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | nginx: 4 | image: "nginx:stable-alpine" 5 | ports: 6 | - "8080:80" 7 | volumes: 8 | - "./tests/static:/usr/share/nginx/html:ro" 9 | selenium: 10 | image: "selenium/standalone-chrome-debug:3.11.0-antimony" 11 | ports: 12 | - "4444:4444" 13 | - "5900:5900" 14 | volumes: 15 | - "./packages/side-recorder/build:/recorder-ext-build" 16 | -------------------------------------------------------------------------------- /dump.rdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeleniumHQ/selenium-ide/5d595cd4c58c504a4b0bc47d3d39c45c636c9c32/dump.rdb -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | globals: { 3 | 'ts-jest': { 4 | tsConfig: 'tsconfig.base.json', 5 | }, 6 | }, 7 | projects: ['/packages/*/jest.config.js'], 8 | testEnvironment: 'jsdom', 9 | testEnvironmentOptions: { 10 | url: 'http://localhost/index.html', 11 | }, 12 | moduleNameMapper: { 13 | '^.+\\.(css|scss)$': 'identity-obj-proxy', 14 | }, 15 | setupFilesAfterEnv: ['./scripts/jest/test.config.js'], 16 | testMatch: ['**/__test?(s)__/**/*.spec.[jt]s?(x)'], 17 | testPathIgnorePatterns: ['/node_modules/'], 18 | transform: { 19 | '^.+\\.jsx?$': 'babel-jest', 20 | '^.+\\.tsx?$': 'ts-jest', 21 | }, 22 | } 23 | -------------------------------------------------------------------------------- /packages/BUILD.bazel: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeleniumHQ/selenium-ide/5d595cd4c58c504a4b0bc47d3d39c45c636c9c32/packages/BUILD.bazel -------------------------------------------------------------------------------- /packages/browser-info/jest-reporter.js: -------------------------------------------------------------------------------- 1 | class BazelReporter { 2 | onRunComplete(_, results) { 3 | if (results.numFailedTests && results.snapshot.failure) { 4 | console.log(`================================================================================ 5 | 6 | Snapshot failed, you can update the snapshot by running 7 | bazel run ${process.env['TEST_TARGET'].replace(/_bin$/, '')}.update 8 | `) 9 | } 10 | } 11 | } 12 | 13 | module.exports = BazelReporter 14 | -------------------------------------------------------------------------------- /packages/browser-info/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testEnvironmentOptions: { 3 | url: 'http://localhost/index.html', 4 | }, 5 | testMatch: ['**/packages/**/__test?(s)__/**/*.spec.[jt]s?(x)'], 6 | testPathIgnorePatterns: ['/node_modules/', '/dist/'], 7 | transform: { 8 | '^.+\\.ts?$': 'ts-jest', 9 | }, 10 | } 11 | -------------------------------------------------------------------------------- /packages/browser-info/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@seleniumhq/browser-info", 3 | "version": "4.0.13", 4 | "private": false, 5 | "description": "Get information about installed browsers", 6 | "author": "Tomer ", 7 | "homepage": "http://github.com/SeleniumHQ/selenium-ide", 8 | "license": "Apache-2.0", 9 | "scripts": { 10 | "build": "tsc", 11 | "clean": "rm -rf dist tsconfig.tsbuildinfo node_modules", 12 | "watch": "tsc --watch" 13 | }, 14 | "main": "dist/index.js", 15 | "types": "dist/index.d.ts", 16 | "files": [ 17 | "dist" 18 | ], 19 | "repository": { 20 | "type": "git", 21 | "url": "git+https://github.com/SeleniumHQ/selenium-ide.git" 22 | }, 23 | "bugs": { 24 | "url": "https://github.com/SeleniumHQ/selenium-ide/issues" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/browser-info/src/__mocks__/sh.ts: -------------------------------------------------------------------------------- 1 | // Licensed to the Software Freedom Conservancy (SFC) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The SFC licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | const { makeError: _m } = jest.requireActual('../sh') 19 | 20 | export const sh = jest.fn() 21 | export const makeError = _m 22 | -------------------------------------------------------------------------------- /packages/browser-info/src/index.ts: -------------------------------------------------------------------------------- 1 | // Licensed to the Software Freedom Conservancy (SFC) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The SFC licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | export * from './chrome' 19 | -------------------------------------------------------------------------------- /packages/browser-info/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "baseUrl": "./src", 5 | "rootDir": "src", 6 | "outDir": "dist", 7 | "forceConsistentCasingInFileNames": true 8 | }, 9 | "include": ["src/**/*.ts"], 10 | "exclude": [ 11 | "**/__mocks__" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /packages/code-export-csharp-commons/README.md: -------------------------------------------------------------------------------- 1 | # code-export-csharp-commons 2 | 3 | Shared code for Selenium IDE C# code exports (e.g., used by NUnit and xUnit exporters). 4 | -------------------------------------------------------------------------------- /packages/code-export-csharp-commons/__test__/src/__snapshots__/location.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`location code emitter should escape quotes in locator strings 1`] = `"By.CssSelector(\\"a[title=\\"escaped\\"]\\")"`; 4 | -------------------------------------------------------------------------------- /packages/code-export-csharp-commons/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@seleniumhq/code-export-csharp-commons", 3 | "version": "4.0.13", 4 | "private": false, 5 | "description": "Commons for exporting Selenium IDE project contents to C#", 6 | "repository": "https://github.com/SeleniumHQ/selenium-ide", 7 | "keywords": [ 8 | "selenium", 9 | "ide", 10 | "export" 11 | ], 12 | "license": "Apache-2.0", 13 | "main": "dist/index.js", 14 | "types": "dist/index.d.ts", 15 | "scripts": { 16 | "build": "tsc", 17 | "clean": "rm -rf dist tsconfig.tsbuildinfo node_modules", 18 | "watch": "tsc --watch" 19 | }, 20 | "dependencies": { 21 | "side-code-export": "^4.0.11" 22 | }, 23 | "devDependencies": { 24 | "@seleniumhq/side-model": "4.0.13" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/code-export-csharp-commons/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "baseUrl": "./src", 5 | "rootDir": "src", 6 | "outDir": "dist" 7 | }, 8 | "include": ["src/**/*.ts"], 9 | "exclude": ["**/__mocks__", "**/__tests__"], 10 | "references": [ 11 | { 12 | "path": "../side-code-export" 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /packages/code-export-csharp-nunit/README.md: -------------------------------------------------------------------------------- 1 | # code-export-csharp-nunit 2 | 3 | NUnit C# code export for Selenium IDE. 4 | -------------------------------------------------------------------------------- /packages/code-export-csharp-nunit/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@seleniumhq/code-export-csharp-nunit", 3 | "version": "4.0.13", 4 | "private": false, 5 | "description": "Export Selenium IDE project contents to C# NUnit", 6 | "repository": "https://github.com/SeleniumHQ/selenium-ide", 7 | "keywords": [ 8 | "selenium", 9 | "ide", 10 | "export" 11 | ], 12 | "license": "Apache-2.0", 13 | "scripts": { 14 | "build": "tsc", 15 | "clean": "rm -rf dist tsconfig.tsbuildinfo node_modules", 16 | "watch": "tsc --watch" 17 | }, 18 | "main": "dist/index.js", 19 | "types": "dist/index.d.ts", 20 | "dependencies": { 21 | "@seleniumhq/code-export-csharp-commons": "4.0.13", 22 | "side-code-export": "^4.0.11" 23 | }, 24 | "gitHead": "f58e327e7616e23a3e926e4b80cf9952164e5744" 25 | } 26 | -------------------------------------------------------------------------------- /packages/code-export-csharp-nunit/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "baseUrl": "./src", 5 | "rootDir": "src", 6 | "outDir": "dist" 7 | }, 8 | "include": ["src/**/*.ts"], 9 | "exclude": ["**/__mocks__", "**/__tests__"], 10 | "references": [ 11 | { 12 | "path": "../code-export-csharp-commons" 13 | }, 14 | { 15 | "path": "../side-code-export" 16 | }, 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /packages/code-export-csharp-xunit/README.md: -------------------------------------------------------------------------------- 1 | # code-export-csharp-xunit 2 | 3 | xUnit C# code export for Selenium IDE. 4 | -------------------------------------------------------------------------------- /packages/code-export-csharp-xunit/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@seleniumhq/code-export-csharp-xunit", 3 | "version": "4.0.13", 4 | "private": false, 5 | "description": "Export Selenium IDE project contents to C# XUnit", 6 | "repository": "https://github.com/SeleniumHQ/selenium-ide", 7 | "keywords": [ 8 | "selenium", 9 | "ide", 10 | "export" 11 | ], 12 | "license": "Apache-2.0", 13 | "scripts": { 14 | "build": "tsc --build --verbose", 15 | "clean": "rm -rf dist tsconfig.tsbuildinfo node_modules", 16 | "watch": "tsc --watch" 17 | }, 18 | "main": "dist/index.js", 19 | "types": "dist/index.d.ts", 20 | "dependencies": { 21 | "@seleniumhq/code-export-csharp-commons": "4.0.13", 22 | "side-code-export": "^4.0.11" 23 | }, 24 | "devDependencies": { 25 | "@seleniumhq/side-model": "4.0.13" 26 | }, 27 | "gitHead": "f58e327e7616e23a3e926e4b80cf9952164e5744" 28 | } 29 | -------------------------------------------------------------------------------- /packages/code-export-csharp-xunit/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "baseUrl": "./src", 5 | "rootDir": "src", 6 | "outDir": "dist" 7 | }, 8 | "include": ["src/**/*.ts", "src/index.ts"], 9 | "exclude": ["**/__mocks__", "**/__tests__"], 10 | "references": [ 11 | { 12 | "path": "../code-export-csharp-commons" 13 | }, 14 | { 15 | "path": "../side-code-export" 16 | }, 17 | { 18 | "path": "../side-model" 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /packages/code-export-java-junit/README.md: -------------------------------------------------------------------------------- 1 | # code-export-java-junit 2 | 3 | Java JUnit code export for Selenium IDE. 4 | -------------------------------------------------------------------------------- /packages/code-export-java-junit/__test__/src/__snapshots__/location.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`location code emitter should escape quotes in locator strings 1`] = `"By.cssSelector(\\"a[title=\\"escaped\\"]\\")"`; 4 | -------------------------------------------------------------------------------- /packages/code-export-java-junit/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@seleniumhq/code-export-java-junit", 3 | "version": "4.0.13", 4 | "private": false, 5 | "description": "Export Selenium IDE project contents to Java JUnit", 6 | "repository": "https://github.com/SeleniumHQ/selenium-ide", 7 | "keywords": [ 8 | "selenium", 9 | "ide", 10 | "export" 11 | ], 12 | "license": "Apache-2.0", 13 | "scripts": { 14 | "build": "tsc", 15 | "clean": "rm -rf dist tsconfig.tsbuildinfo node_modules", 16 | "watch": "tsc --watch" 17 | }, 18 | "main": "dist/index.js", 19 | "dependencies": { 20 | "side-code-export": "^4.0.11" 21 | }, 22 | "devDependencies": { 23 | "@seleniumhq/side-model": "4.0.13" 24 | }, 25 | "gitHead": "f58e327e7616e23a3e926e4b80cf9952164e5744" 26 | } 27 | -------------------------------------------------------------------------------- /packages/code-export-java-junit/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "baseUrl": "./src", 5 | "rootDir": "src", 6 | "outDir": "dist" 7 | }, 8 | "include": ["src/**/*.ts"], 9 | "exclude": ["**/__mocks__", "**/__tests__"], 10 | "references": [ 11 | { 12 | "path": "../side-code-export" 13 | }, 14 | { 15 | "path": "../side-model" 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /packages/code-export-javascript-mocha/README.md: -------------------------------------------------------------------------------- 1 | # code-export-javascript-mocha 2 | 3 | JavaScript Mocha code export for Selenium IDE. 4 | -------------------------------------------------------------------------------- /packages/code-export-javascript-mocha/__test__/src/__snapshots__/location.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`location code emitter should escape quotes in locator strings 1`] = `"By.css(\\"a[title=\\"escaped\\"]\\")"`; 4 | -------------------------------------------------------------------------------- /packages/code-export-javascript-mocha/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@seleniumhq/code-export-javascript-mocha", 3 | "version": "4.0.13", 4 | "private": false, 5 | "description": "Export Selenium IDE project contents to JavaScript Mocha", 6 | "repository": "https://github.com/SeleniumHQ/selenium-ide", 7 | "keywords": [ 8 | "selenium", 9 | "ide", 10 | "export" 11 | ], 12 | "license": "Apache-2.0", 13 | "scripts": { 14 | "build": "tsc", 15 | "clean": "rm -rf dist tsconfig.tsbuildinfo node_modules", 16 | "watch": "tsc --watch" 17 | }, 18 | "main": "dist/index.js", 19 | "dependencies": { 20 | "side-code-export": "^4.0.11" 21 | }, 22 | "devDependencies": { 23 | "@seleniumhq/side-model": "4.0.13" 24 | }, 25 | "gitHead": "f58e327e7616e23a3e926e4b80cf9952164e5744" 26 | } 27 | -------------------------------------------------------------------------------- /packages/code-export-javascript-mocha/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "baseUrl": "./src", 5 | "rootDir": "src", 6 | "outDir": "dist" 7 | }, 8 | "include": ["src/**/*.ts"], 9 | "exclude": ["**/__mocks__", "**/__tests__"], 10 | "references": [ 11 | { 12 | "path": "../side-code-export" 13 | }, 14 | { 15 | "path": "../side-model" 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /packages/code-export-python-pytest/README.md: -------------------------------------------------------------------------------- 1 | # code-export-python-pytest 2 | 3 | Python pytest code export for Selenium IDE. 4 | -------------------------------------------------------------------------------- /packages/code-export-python-pytest/__test__/src/__snapshots__/location.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`location code emitter should escape quotes in locator strings 1`] = `"By.CSS_SELECTOR, \\"a[title=\\"escaped\\"]\\""`; 4 | -------------------------------------------------------------------------------- /packages/code-export-python-pytest/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@seleniumhq/code-export-python-pytest", 3 | "version": "4.0.13", 4 | "private": false, 5 | "description": "Export Selenium IDE project contents to Python pytest", 6 | "repository": "https://github.com/SeleniumHQ/selenium-ide", 7 | "keywords": [ 8 | "selenium", 9 | "ide", 10 | "export" 11 | ], 12 | "license": "Apache-2.0", 13 | "scripts": { 14 | "build": "tsc", 15 | "clean": "rm -rf dist tsconfig.tsbuildinfo node_modules", 16 | "watch": "tsc --watch" 17 | }, 18 | "main": "dist/index.js", 19 | "dependencies": { 20 | "side-code-export": "^4.0.11" 21 | }, 22 | "devDependencies": { 23 | "@seleniumhq/side-model": "4.0.13" 24 | }, 25 | "gitHead": "f58e327e7616e23a3e926e4b80cf9952164e5744" 26 | } 27 | -------------------------------------------------------------------------------- /packages/code-export-python-pytest/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "baseUrl": "./src", 5 | "rootDir": "src", 6 | "outDir": "dist" 7 | }, 8 | "include": ["src/**/*.ts"], 9 | "exclude": ["**/__mocks__", "**/__tests__"], 10 | "references": [ 11 | { 12 | "path": "../side-code-export" 13 | }, 14 | { 15 | "path": "../side-model" 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /packages/code-export-ruby-rspec/README.md: -------------------------------------------------------------------------------- 1 | # code-export-ruby-rspec 2 | 3 | Ruby RSpec code export for Selenium IDE. 4 | -------------------------------------------------------------------------------- /packages/code-export-ruby-rspec/__test__/src/__snapshots__/selection.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`selection location code emitter should emit id locator 1`] = `":css, \\"*[id='someId']\\""`; 4 | 5 | exports[`selection location code emitter should emit index locator 1`] = `":css, '*:nth-child(2)'"`; 6 | 7 | exports[`selection location code emitter should emit label locator 1`] = `":xpath, \\"//option[. = 'a label']\\""`; 8 | 9 | exports[`selection location code emitter should emit value locator 1`] = `":css, \\"*[value='someValue']\\""`; 10 | -------------------------------------------------------------------------------- /packages/code-export-ruby-rspec/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@seleniumhq/code-export-ruby-rspec", 3 | "version": "4.0.13", 4 | "private": false, 5 | "description": "Export Selenium IDE project contents to Ruby RSpec", 6 | "repository": "https://github.com/SeleniumHQ/selenium-ide", 7 | "keywords": [ 8 | "selenium", 9 | "ide", 10 | "export" 11 | ], 12 | "license": "Apache-2.0", 13 | "scripts": { 14 | "build": "tsc", 15 | "clean": "rm -rf dist tsconfig.tsbuildinfo node_modules", 16 | "watch": "tsc --watch" 17 | }, 18 | "main": "dist/index.js", 19 | "dependencies": { 20 | "side-code-export": "^4.0.11" 21 | }, 22 | "devDependencies": { 23 | "@seleniumhq/side-model": "4.0.13" 24 | }, 25 | "gitHead": "f58e327e7616e23a3e926e4b80cf9952164e5744" 26 | } 27 | -------------------------------------------------------------------------------- /packages/code-export-ruby-rspec/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "baseUrl": "./src", 5 | "rootDir": "src", 6 | "outDir": "dist" 7 | }, 8 | "include": ["src/**/*.ts"], 9 | "exclude": ["**/__mocks__", "**/__tests__"], 10 | "references": [ 11 | { 12 | "path": "../side-code-export" 13 | }, 14 | { 15 | "path": "../side-model" 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /packages/get-driver/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testEnvironmentOptions: { 3 | url: 'http://localhost/index.html', 4 | }, 5 | testMatch: ['**/packages/**/__test?(s)__/**/*.spec.[jt]s?(x)'], 6 | testPathIgnorePatterns: ['/node_modules/', '/dist/'], 7 | transform: { 8 | '^.+\\.ts?$': 'ts-jest', 9 | }, 10 | } 11 | -------------------------------------------------------------------------------- /packages/get-driver/src/index.ts: -------------------------------------------------------------------------------- 1 | // Licensed to the Software Freedom Conservancy (SFC) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The SFC licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | export { default as downloadDriver } from './download-driver' 19 | export * from './resolve-driver' 20 | export * from './types' 21 | -------------------------------------------------------------------------------- /packages/get-driver/src/types.ts: -------------------------------------------------------------------------------- 1 | export type Arch = 2 | | 'arm' 3 | | 'arm64' 4 | | 'ia32' 5 | | 'mips' 6 | | 'mipsel' 7 | | 'ppc' 8 | | 'ppc64' 9 | | 's390' 10 | | 's390x' 11 | | 'x32' 12 | | 'x64' 13 | -------------------------------------------------------------------------------- /packages/get-driver/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "baseUrl": "./src", 5 | "rootDir": "src", 6 | "outDir": "dist", 7 | "strictNullChecks": true 8 | }, 9 | "include": ["src/**/*.ts"], 10 | "exclude": ["**/__mocks__"] 11 | } 12 | -------------------------------------------------------------------------------- /packages/selenium-ide/.eslintrc.js: -------------------------------------------------------------------------------- 1 | const pkg = import('./package.json') 2 | 3 | module.exports = { 4 | extends: './../../.eslintrc.js', 5 | rules: { 6 | 'node/no-missing-import': ['off'], 7 | 'node/no-unpublished-import': [ 8 | 'error', 9 | { 10 | allowModules: Object.keys(pkg.devDependencies), 11 | }, 12 | ], 13 | 'import/no-unresolved': ['off'], 14 | }, 15 | } 16 | -------------------------------------------------------------------------------- /packages/selenium-ide/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "stop chromedriver", 6 | "command": "taskkill", 7 | "type": "process", 8 | "args": [ 9 | "/im", 10 | "chromedriver.exe", 11 | "/f", 12 | "/FI", 13 | "IMAGENAME eq chromedriver.exe" 14 | ], 15 | "presentation": { 16 | "reveal": "always" 17 | } 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /packages/selenium-ide/README.md: -------------------------------------------------------------------------------- 1 | # Selenium IDE v4 2 | 3 | This is an electron app instrumenting the electron-chromedriver package to run 4 | a chromedriver instance on the Electron browser. 5 | 6 | There are three regions to the code: 7 | 8 | 1. Main: The electron process, where the NodeJS backend handles the system ops: 9 | 10 | * Spawning electron BrowserWindows 11 | * Filesystem operations 12 | * Persists file edit operations 13 | 14 | 2. Browser: The renderering code for the electron browser windows. 15 | 16 | * Project editor window 17 | * Project selector window 18 | * Playback window 19 | 20 | 3. API: The imported side-api package. This contains common code used by the 21 | main and browser segments. 22 | 23 | This is separated out to make importing types for plugins not require the entire 24 | dependency tree of selenium-ide, which involves electron and react and is quite 25 | extensive. 26 | -------------------------------------------------------------------------------- /packages/selenium-ide/resources/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeleniumHQ/selenium-ide/5d595cd4c58c504a4b0bc47d3d39c45c636c9c32/packages/selenium-ide/resources/background.png -------------------------------------------------------------------------------- /packages/selenium-ide/resources/background@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeleniumHQ/selenium-ide/5d595cd4c58c504a4b0bc47d3d39c45c636c9c32/packages/selenium-ide/resources/background@2x.png -------------------------------------------------------------------------------- /packages/selenium-ide/resources/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeleniumHQ/selenium-ide/5d595cd4c58c504a4b0bc47d3d39c45c636c9c32/packages/selenium-ide/resources/icon.icns -------------------------------------------------------------------------------- /packages/selenium-ide/resources/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeleniumHQ/selenium-ide/5d595cd4c58c504a4b0bc47d3d39c45c636c9c32/packages/selenium-ide/resources/icon.ico -------------------------------------------------------------------------------- /packages/selenium-ide/resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeleniumHQ/selenium-ide/5d595cd4c58c504a4b0bc47d3d39c45c636c9c32/packages/selenium-ide/resources/icon.png -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/I18N/keys.ts: -------------------------------------------------------------------------------- 1 | import en from './en' 2 | import {transformNestedObject} from './util' 3 | 4 | /** 5 | * Take this nested object, keep the shape, but make the final keys the dot delimited string 6 | * path of the nested key 7 | */ 8 | const languageMap = transformNestedObject((key) => key, en) 9 | 10 | export default languageMap 11 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/api/bidi.ts: -------------------------------------------------------------------------------- 1 | import type { Api } from '@seleniumhq/side-api' 2 | import { processApi } from '@seleniumhq/side-api' 3 | import EventListener from './classes/DriverEventListener' 4 | import Handler from './classes/DriverHandler' 5 | 6 | const api: Api = processApi((path: string) => { 7 | const trailingSegment: string = path.split('.').pop() as string 8 | if (trailingSegment.startsWith('on')) { 9 | return EventListener()(path) 10 | } 11 | return Handler()(path) 12 | }) 13 | 14 | export { BrowserApiMutators } from './mutator' 15 | export { Api } from '@seleniumhq/side-api' 16 | 17 | export default api 18 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/api/classes/DriverUtils.ts: -------------------------------------------------------------------------------- 1 | export const UniqueIdentifier = '!!!SELENIUM_IDE_IDENTIFIER_HERE!!!' 2 | 3 | type Message = { 4 | path: string 5 | args: any[] 6 | } 7 | 8 | let id = 0 9 | export const sendMessage = (message: Message) => { 10 | const nextID = id++ 11 | console.log(UniqueIdentifier + JSON.stringify({ ...message, id: nextID })) 12 | return nextID 13 | } -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/api/index.ts: -------------------------------------------------------------------------------- 1 | import type { Api } from '@seleniumhq/side-api' 2 | import { processApi } from '@seleniumhq/side-api' 3 | import EventListener from './classes/EventListener' 4 | import Handler from './classes/Handler' 5 | 6 | const api: Api = processApi((path: string) => { 7 | const trailingSegment: string = path.split('.').pop() as string 8 | if (trailingSegment.startsWith('on')) { 9 | return EventListener()(path) 10 | } 11 | return Handler()(path) 12 | }) 13 | 14 | export {BrowserApiMutators} from './mutator' 15 | export { Api } from '@seleniumhq/side-api' 16 | 17 | export default api 18 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/api/mutator.ts: -------------------------------------------------------------------------------- 1 | import { processApi } from '@seleniumhq/side-api' 2 | import { ApiHoist as Api } from '@seleniumhq/side-api' 3 | 4 | type FunctionKeys> = { 5 | [K in keyof T as T[K]['mutator'] extends Function ? K : never]: T[K]['mutator']; 6 | }; 7 | 8 | /** 9 | * This Converts the chrome API type to something usable 10 | * from the front end 11 | */ 12 | export type BrowserApiMutators = { 13 | [Namespace in keyof Api]: FunctionKeys 14 | } 15 | 16 | export default processApi( 17 | (_, handler) => handler.mutator 18 | ) 19 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/components/AppBar/AppBarCore.tsx: -------------------------------------------------------------------------------- 1 | import Box, { BoxProps } from '@mui/material/Box' 2 | import { styled } from '@mui/material/styles' 3 | 4 | const AppBarCore: React.FC = styled(Box)(({ theme }) => ({ 5 | display: 'flex', 6 | flexDirection: 'row', 7 | transition: theme.transitions.create(['margin', 'width'], { 8 | easing: theme.transitions.easing.sharp, 9 | duration: theme.transitions.duration.leavingScreen, 10 | }), 11 | })) 12 | 13 | export default AppBarCore 14 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/components/AppPanelWrapper.tsx: -------------------------------------------------------------------------------- 1 | import { Paper } from '@mui/material' 2 | import React, { FC } from 'react' 3 | import AppWrapper from './AppWrapper' 4 | import PanelNav from './PanelNav' 5 | 6 | interface AppPanelWrapperProps extends React.HTMLAttributes { 7 | horizontal?: boolean 8 | } 9 | 10 | const AppPanelWrapper: FC = ({ 11 | children, 12 | horizontal, 13 | }) => { 14 | const navClassName = React.useMemo( 15 | () => `fill ${horizontal ? 'outside-v-nav' : 'outside-nav'}`, 16 | [horizontal] 17 | ) 18 | return ( 19 | 20 | 21 | 22 | {children} 23 | 24 | 25 | ) 26 | } 27 | 28 | export default AppPanelWrapper 29 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/components/Controls/BaseProps.tsx: -------------------------------------------------------------------------------- 1 | const baseControlProps = { 2 | className: 'm-2 not-draggable', 3 | color: 'inherit', 4 | sx: { borderRadius: 0.5 }, 5 | } as const 6 | 7 | export default baseControlProps -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/components/Controls/PauseButton.tsx: -------------------------------------------------------------------------------- 1 | import PauseIcon from '@mui/icons-material/Pause' 2 | import IconButton from '@mui/material/IconButton' 3 | import Tooltip from '@mui/material/Tooltip' 4 | import React, { FC } from 'react' 5 | import baseControlProps from './BaseProps' 6 | import { FormattedMessage } from 'react-intl' 7 | import languageMap from 'browser/I18N/keys' 8 | 9 | const PauseButton: FC = () => ( 10 | } 12 | aria-label="pause" 13 | > 14 | window.sideAPI.playback.pause()} 18 | > 19 | 20 | 21 | 22 | ) 23 | 24 | export default PauseButton 25 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/components/Controls/PlayListButton.tsx: -------------------------------------------------------------------------------- 1 | import PlaylistPlayIcon from '@mui/icons-material/PlaylistPlay' 2 | import IconButton from '@mui/material/IconButton' 3 | import Tooltip from '@mui/material/Tooltip' 4 | import React, { FC } from 'react' 5 | import baseControlProps from './BaseProps' 6 | import { FormattedMessage } from 'react-intl' 7 | import languageMap from 'browser/I18N/keys' 8 | 9 | const PlayListButton: FC = () => ( 10 | } 12 | aria-label="play-suite" 13 | > 14 | window.sideAPI.playback.playSuite()} 18 | > 19 | 20 | 21 | 22 | ) 23 | 24 | export default PlayListButton 25 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/components/Controls/RecordButton.tsx: -------------------------------------------------------------------------------- 1 | import RecordIcon from '@mui/icons-material/FiberManualRecord' 2 | import IconButton from '@mui/material/IconButton' 3 | import Tooltip from '@mui/material/Tooltip' 4 | import React, { FC } from 'react' 5 | import baseControlProps from './BaseProps' 6 | import { FormattedMessage } from 'react-intl' 7 | import languageMap from 'browser/I18N/keys' 8 | 9 | const RecordButton: FC = () => ( 10 | } 12 | aria-label="record" 13 | > 14 | window.sideAPI.recorder.start()} 18 | > 19 | 20 | 21 | 22 | ) 23 | 24 | export default RecordButton 25 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/components/Controls/StopButton.tsx: -------------------------------------------------------------------------------- 1 | import StopIcon from '@mui/icons-material/Stop' 2 | import IconButton from '@mui/material/IconButton' 3 | import Tooltip from '@mui/material/Tooltip' 4 | import React, { FC } from 'react' 5 | import baseControlProps from './BaseProps' 6 | import languageMap from 'browser/I18N/keys' 7 | import { FormattedMessage } from 'react-intl' 8 | 9 | const StopButton: FC = () => ( 10 | } 12 | aria-label="stop" 13 | > 14 | window.sideAPI.playback.stop()} 17 | > 18 | 19 | 20 | 21 | ) 22 | 23 | export default StopButton 24 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/components/Drawer/Header.tsx: -------------------------------------------------------------------------------- 1 | import Paper, {PaperProps} from '@mui/material/Paper' 2 | import { styled } from '@mui/material/styles' 3 | 4 | const DrawerHeader: React.FC = styled(Paper)( 5 | ({ theme }) => ({ 6 | alignItems: 'center', 7 | borderColor: theme.palette.action.focus, 8 | // necessary for content to be below app bar 9 | justifyContent: 'flex-end', 10 | textOverflow: 'ellipsis', 11 | }) 12 | ) 13 | 14 | export default DrawerHeader 15 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/components/Drawer/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import TabPanel from '../Tab/Panel' 3 | import { PROJECT_TAB, SUITES_TAB, TESTS_TAB } from '../../enums/tab' 4 | import { SIDEMainProps } from '../types' 5 | import TestsDrawer from 'browser/windows/ProjectEditor/tabs/Tests/TestsDrawer' 6 | import SuitesDrawer from 'browser/windows/ProjectEditor/tabs/Suites/SuitesDrawer' 7 | import ProjectDrawer from 'browser/windows/ProjectEditor/tabs/Project/ProjectDrawer' 8 | 9 | const SIDEDrawer: React.FC> = ({ 10 | tab, 11 | }) => ( 12 | <> 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | ) 24 | 25 | export default SIDEDrawer 26 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/components/Main/Header.tsx: -------------------------------------------------------------------------------- 1 | import Box, {BoxProps} from '@mui/material/Box' 2 | import { styled } from '@mui/material/styles' 3 | 4 | const MainHeader: React.FC = styled(Box)(() => ({ 5 | alignItems: 'center', 6 | display: 'flex', 7 | // necessary for content to be below app bar 8 | height: '48px !important', 9 | minHeight: '48px !important', 10 | position: 'sticky', 11 | top: 0, 12 | zIndex: 100, 13 | })) 14 | 15 | export default MainHeader 16 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/components/Main/MainCore.tsx: -------------------------------------------------------------------------------- 1 | import { styled } from '@mui/material' 2 | import React, { HTMLAttributes } from 'react' 3 | 4 | const MainCore: React.FC> = styled('div')(({ theme }) => ({ 5 | flexDirection: 'column', 6 | flexGrow: 1, 7 | transition: theme.transitions.create('margin', { 8 | easing: theme.transitions.easing.sharp, 9 | duration: theme.transitions.duration.leavingScreen, 10 | }), 11 | })) 12 | 13 | export default MainCore 14 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/components/Main/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import TabPanel from '../Tab/Panel' 3 | import { PROJECT_TAB, SUITES_TAB, TESTS_TAB } from '../../enums/tab' 4 | import ProjectTab from '../../windows/ProjectEditor/tabs/Project/ProjectTab' 5 | import SuitesTab from '../../windows/ProjectEditor/tabs/Suites/SuitesTab' 6 | import TestsTab from '../../windows/ProjectEditor/tabs/Tests/TestsTab' 7 | import { SIDEMainProps } from '../types' 8 | 9 | const SIDEMain: React.FC> = ({ 10 | setTab, 11 | tab, 12 | }) => ( 13 | <> 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | ) 25 | 26 | export default SIDEMain 27 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/components/PanelNav.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Paper } from '@mui/material' 3 | 4 | interface PanelNavProps extends React.HTMLAttributes { 5 | horizontal?: boolean 6 | } 7 | 8 | export const NavSize = 14 9 | 10 | const PanelNav: React.FC = ({ children, horizontal }) => ( 11 | 18 | 19 | {children} 20 | 21 | 22 | ) 23 | 24 | export default PanelNav 25 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/components/PlaybackPanel/index.tsx: -------------------------------------------------------------------------------- 1 | import Box from '@mui/material/Box' 2 | import Paper from '@mui/material/Paper' 3 | import Typography from '@mui/material/Typography' 4 | import languageMap from 'browser/I18N/keys' 5 | import React from 'react' 6 | import { FormattedMessage } from 'react-intl' 7 | 8 | const ProjectPlaybackWindow = () => ( 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | ) 21 | 22 | export default ProjectPlaybackWindow 23 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/components/PlaybackTabBar/index.tsx: -------------------------------------------------------------------------------- 1 | import Paper from '@mui/material/Paper' 2 | import React from 'react' 3 | import PlaybackTab, { TabShape } from './tab' 4 | import IconButton from '@mui/material/IconButton' 5 | import { Add } from '@mui/icons-material' 6 | import baseControlProps from '../Controls/BaseProps' 7 | 8 | const { 9 | windows: { requestPlaybackWindow }, 10 | } = window.sideAPI 11 | 12 | const tabBarSX = { 13 | borderBottom: 1, 14 | borderColor: 'grey.500', 15 | height: 38, 16 | } 17 | 18 | const PlaybackTabBar: React.FC<{ tabs: TabShape[] }> = ({ tabs }) => ( 19 | 25 | {tabs.map((tab) => ( 26 | 27 | ))} 28 | requestPlaybackWindow()}> 29 | 30 | 31 | 32 | ) 33 | 34 | export default PlaybackTabBar 35 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/components/ReorderableList.tsx: -------------------------------------------------------------------------------- 1 | import { TableBodyProps } from '@mui/material/TableBody' 2 | import List, { ListProps } from '@mui/material/List' 3 | import React, { FC } from 'react' 4 | 5 | export type ReorderableListProps = ListProps & TableBodyProps & { 6 | Component?: React.FC | React.ComponentClass 7 | } 8 | 9 | const ReorderableList: FC = ({ 10 | children, 11 | Component = List, 12 | sx = {}, 13 | ...props 14 | }) => ( 15 | 24 | {children} 25 | 26 | ) 27 | 28 | export default ReorderableList 29 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/components/ResizeHandle/index.tsx: -------------------------------------------------------------------------------- 1 | import Paper from '@mui/material/Paper' 2 | import React from 'react' 3 | import { PanelResizeHandle } from 'react-resizable-panels' 4 | 5 | export type PluginWindow = { 6 | name: string 7 | url: string 8 | } 9 | 10 | const sx = { 11 | backgroundColor: 'primary.main', 12 | } 13 | 14 | type ResizeHandleProps = { 15 | id: string 16 | x?: boolean 17 | y?: boolean 18 | } 19 | 20 | const ResizeHandle: React.FC = ({ 21 | id, 22 | x = false, 23 | y = false, 24 | }) => ( 25 | 26 | 29 | ['resize-handle', x ? 'x' : '', y ? 'y' : ''] 30 | .filter(Boolean) 31 | .join(' '), 32 | [x, y] 33 | )} 34 | sx={sx} 35 | variant="outlined" 36 | /> 37 | 38 | ) 39 | 40 | export default ResizeHandle 41 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/components/Tab/Panel.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | interface TabPanelProps { 4 | children?: React.ReactElement 5 | dir?: string 6 | index: number 7 | value: number 8 | } 9 | 10 | const TabPanel = ({ 11 | children, 12 | value, 13 | index, 14 | }: TabPanelProps): React.ReactElement | null => 15 | value === index ? (children as React.ReactElement) : null 16 | 17 | export default TabPanel 18 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/components/Tab/PanelMulti.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | interface TabPanelMultiProps { 4 | children?: React.ReactElement 5 | indexes: number[] 6 | value: number 7 | } 8 | 9 | const TabPanelMulti = ({ children, indexes, value }: TabPanelMultiProps): React.ReactElement | null => 10 | indexes.includes(value) ? (children as React.ReactElement) : null 11 | 12 | export default TabPanelMulti 13 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/components/UncontrolledTextField.tsx: -------------------------------------------------------------------------------- 1 | import TextField, { TextFieldProps } from '@mui/material/TextField' 2 | import { noop } from 'lodash/fp' 3 | import React, { FC } from 'react' 4 | 5 | /** 6 | * This is actually a hyper controlled text field 7 | */ 8 | const UncontrolledTextField: FC = ({ 9 | value = '', 10 | onChange: _onChange = noop, 11 | ...props 12 | }) => { 13 | const [localValue, setLocalValue] = React.useState(value) 14 | React.useEffect(() => { 15 | setLocalValue(value) 16 | }, [props.id]) 17 | const onChange: React.ChangeEventHandler = (e) => { 18 | const el = e.target as HTMLInputElement 19 | setLocalValue(el.value) 20 | _onChange(e) 21 | } 22 | return ( 23 | 29 | ) 30 | } 31 | 32 | export default UncontrolledTextField 33 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/components/types.ts: -------------------------------------------------------------------------------- 1 | import { CoreSessionData } from '@seleniumhq/side-api' 2 | import React from 'react' 3 | import { TAB } from '../enums/tab' 4 | 5 | export interface SIDEMainProps { 6 | session: CoreSessionData 7 | setTab: React.Dispatch> 8 | tab: number 9 | } 10 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/contexts/active-command.ts: -------------------------------------------------------------------------------- 1 | import { 2 | CoreSessionData, 3 | getActiveCommand, 4 | } from '@seleniumhq/side-api' 5 | import { defaultSession } from 'browser/helpers/subscribeToSession' 6 | import React from 'react' 7 | 8 | export const transform = (data: CoreSessionData) => { 9 | const activeCommand = getActiveCommand(data) 10 | return activeCommand 11 | } 12 | 13 | export const context = React.createContext( 14 | transform(defaultSession) 15 | ) 16 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/contexts/active-test.ts: -------------------------------------------------------------------------------- 1 | import { CoreSessionData } from '@seleniumhq/side-api' 2 | import { defaultSession } from 'browser/helpers/subscribeToSession' 3 | import React from 'react' 4 | 5 | type ActiveTestContext = { 6 | activeSuiteID: string 7 | activeTestID: string 8 | } 9 | 10 | export const transform = (data: CoreSessionData): ActiveTestContext => ({ 11 | activeSuiteID: data.state.activeSuiteID, 12 | activeTestID: data.state.activeTestID, 13 | }) 14 | 15 | export const context = React.createContext( 16 | transform(defaultSession) 17 | ) 18 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/contexts/command-map.ts: -------------------------------------------------------------------------------- 1 | import { CoreSessionData } from '@seleniumhq/side-api' 2 | import { defaultSession } from 'browser/helpers/subscribeToSession' 3 | import React from 'react' 4 | 5 | export const transform = (data: CoreSessionData) => data.state.commands 6 | 7 | export const context = React.createContext(transform(defaultSession)) 8 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/contexts/config-settings-group.ts: -------------------------------------------------------------------------------- 1 | import { ConfigSettingsGroup, CoreSessionData } from '@seleniumhq/side-api' 2 | import { defaultSession } from 'browser/helpers/subscribeToSession' 3 | import React from 'react' 4 | 5 | export const transform = (data: CoreSessionData) => 6 | data.state.editor.configSettingsGroup 7 | 8 | export const context = React.createContext( 9 | transform(defaultSession) 10 | ) 11 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/contexts/config.ts: -------------------------------------------------------------------------------- 1 | import { CoreSessionData } from '@seleniumhq/side-api' 2 | import { defaultSession } from 'browser/helpers/subscribeToSession' 3 | import React from 'react' 4 | 5 | type ConfigShape = { 6 | project: { 7 | delay?: number 8 | name: string 9 | plugins: string[] 10 | timeout?: number 11 | url: string 12 | } 13 | system: CoreSessionData['state']['userPrefs'] 14 | } 15 | 16 | export const transform = (data: CoreSessionData): ConfigShape => ({ 17 | project: { 18 | delay: data.project.delay, 19 | name: data.project.name, 20 | plugins: data.project.plugins, 21 | timeout: data.project.timeout, 22 | url: data.project.url, 23 | }, 24 | system: data.state.userPrefs, 25 | }) 26 | 27 | export const context = React.createContext( 28 | transform(defaultSession) 29 | ) 30 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/contexts/playback-command-states.ts: -------------------------------------------------------------------------------- 1 | import { CommandsStateShape, CoreSessionData } from '@seleniumhq/side-api' 2 | import { defaultSession } from 'browser/helpers/subscribeToSession' 3 | import React from 'react' 4 | 5 | export const transform = (data: CoreSessionData) => data.state.playback.commands 6 | 7 | export const context = React.createContext( 8 | transform(defaultSession) 9 | ) 10 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/contexts/playback-current-index.ts: -------------------------------------------------------------------------------- 1 | import { CoreSessionData } from '@seleniumhq/side-api' 2 | import { defaultSession } from 'browser/helpers/subscribeToSession' 3 | import React from 'react' 4 | 5 | export const transform = (data: CoreSessionData) => { 6 | return data.state.playback.currentIndex 7 | } 8 | 9 | export const context = React.createContext(transform(defaultSession)) 10 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/contexts/playback-test-results.ts: -------------------------------------------------------------------------------- 1 | import { CoreSessionData } from '@seleniumhq/side-api' 2 | import { defaultSession } from 'browser/helpers/subscribeToSession' 3 | import React from 'react' 4 | 5 | export const transform = (data: CoreSessionData) => 6 | data.state.playback.testResults 7 | 8 | export const context = React.createContext(transform(defaultSession)) 9 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/contexts/session.ts: -------------------------------------------------------------------------------- 1 | import { CoreSessionData } from '@seleniumhq/side-api' 2 | import { defaultSession } from 'browser/helpers/subscribeToSession' 3 | import React from 'react' 4 | 5 | export const transform = (data: CoreSessionData) => { 6 | return data 7 | } 8 | 9 | export const context = React.createContext( 10 | transform(defaultSession) 11 | ) 12 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/contexts/show-drawer.ts: -------------------------------------------------------------------------------- 1 | import { CoreSessionData } from '@seleniumhq/side-api' 2 | import { defaultSession } from 'browser/helpers/subscribeToSession' 3 | import React from 'react' 4 | 5 | export const transform = (data: CoreSessionData) => { 6 | return data.state.editor.showDrawer 7 | } 8 | 9 | export const context = React.createContext(transform(defaultSession)) 10 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/contexts/status.ts: -------------------------------------------------------------------------------- 1 | import { CoreSessionData } from '@seleniumhq/side-api' 2 | import { defaultSession } from 'browser/helpers/subscribeToSession' 3 | import React from 'react' 4 | 5 | export const transform = (data: CoreSessionData) => { 6 | return data.state.status 7 | } 8 | 9 | export const context = React.createContext(transform(defaultSession)) 10 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/contexts/suite-mode.ts: -------------------------------------------------------------------------------- 1 | import { CoreSessionData } from '@seleniumhq/side-api' 2 | import { defaultSession } from 'browser/helpers/subscribeToSession' 3 | import React from 'react' 4 | 5 | export const transform = (data: CoreSessionData) => { 6 | return data.state.editor.suiteMode 7 | } 8 | 9 | export const context = React.createContext(transform(defaultSession)) 10 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/contexts/suites.ts: -------------------------------------------------------------------------------- 1 | import { CoreSessionData } from '@seleniumhq/side-api' 2 | import { defaultSession } from 'browser/helpers/subscribeToSession' 3 | import React from 'react' 4 | 5 | export const transform = (data: CoreSessionData) => { 6 | return data.project.suites 7 | } 8 | 9 | export const context = React.createContext(transform(defaultSession)) 10 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/contexts/tests.ts: -------------------------------------------------------------------------------- 1 | import { CoreSessionData } from '@seleniumhq/side-api' 2 | import { defaultSession } from 'browser/helpers/subscribeToSession' 3 | import React from 'react' 4 | 5 | export const transform = (data: CoreSessionData) => { 6 | return data.project.tests 7 | } 8 | 9 | export const context = React.createContext(transform(defaultSession)) 10 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/enums/tab.ts: -------------------------------------------------------------------------------- 1 | export const TESTS_TAB = 0 2 | export const SUITES_TAB = 1 3 | export const PROJECT_TAB = 2 4 | 5 | export type TAB = typeof TESTS_TAB | typeof SUITES_TAB | typeof PROJECT_TAB 6 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/helpers/getEntryColor.ts: -------------------------------------------------------------------------------- 1 | const getEntryColor = ({ id }: { id: string }, activeID: string | null) => 2 | id === activeID ? 'bg-blue-100' : 'bg-gray-100' 3 | 4 | export default getEntryColor 5 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/helpers/preload-bidi.ts: -------------------------------------------------------------------------------- 1 | import preload from './preload' 2 | import api from 'browser/api/bidi' 3 | 4 | export default preload(api) -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/helpers/renderWhenReady.ts: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {createRoot} from 'react-dom/client' 3 | 4 | export default (Component: React.FC | React.ComponentClass) => { 5 | document.addEventListener('DOMContentLoaded', () => { 6 | const domContainer = document.querySelector('#root') 7 | const root = createRoot(domContainer!) 8 | root.render(React.createElement(Component)) 9 | }); 10 | } -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/helpers/string.ts: -------------------------------------------------------------------------------- 1 | export const camelToTitleCase = (text: string) => { 2 | const result = text.replace(/([A-Z])/g, ' $1') 3 | return result.charAt(0).toUpperCase() + result.slice(1) 4 | } 5 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/helpers/useHeightFromElement.ts: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react' 2 | 3 | export const useHeightFromElement = (id: string): number => { 4 | const [bottomOffset, setBottomOffset] = React.useState(0) 5 | 6 | useEffect(() => { 7 | if (!id) return; 8 | const element = document.getElementById(id) as HTMLElement 9 | const observer = new MutationObserver(() => { 10 | console.log('Mutating?', element.clientHeight) 11 | setBottomOffset(element.clientHeight ?? 0) 12 | }) 13 | observer.observe(element, { childList: true, subtree: true }) 14 | return () => observer.disconnect() 15 | }, [id]) 16 | return bottomOffset 17 | } 18 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/hooks/usePanelGroup.ts: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { ImperativePanelGroupHandle } from 'react-resizable-panels' 3 | 4 | export const usePanelGroup = (id: string, disabled = false) => { 5 | const [ready, setReady] = React.useState(false) 6 | const ref = React.useRef(null) 7 | React.useEffect(() => { 8 | if (disabled) { 9 | return setReady(false) 10 | } 11 | if (!ref.current) return 12 | window.sideAPI.resizablePanels.getPanelGroup(id).then((values) => { 13 | ref.current?.setLayout(values) 14 | setReady(true) 15 | }) 16 | }, [disabled, ref]) 17 | const onLayout = React.useCallback( 18 | (sizes: number[]) => { 19 | if (!ready) return 20 | window.sideAPI.resizablePanels.setPanelGroup(id, sizes) 21 | }, 22 | [ready] 23 | ) 24 | return { 25 | ref, 26 | reset: () => setReady(false), 27 | onLayout, 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/windows/Alert/controller.ts: -------------------------------------------------------------------------------- 1 | import { WindowConfig } from 'browser/types' 2 | 3 | const DEFAULT_WIDTH = 400 4 | const DEFAULT_HEIGHT = 150 5 | 6 | export const window: WindowConfig['window'] = () => ({ 7 | width: DEFAULT_WIDTH, 8 | height: DEFAULT_HEIGHT, 9 | minWidth: DEFAULT_WIDTH, 10 | minHeight: DEFAULT_HEIGHT, 11 | resizable: false, 12 | minimizable: false, 13 | fullscreenable: false, 14 | autoHideMenuBar: true, 15 | maximizable: false, 16 | show: false, 17 | skipTaskbar: true, 18 | useContentSize: false, 19 | modal: true, 20 | title: 'Window Prompt Polyfill', 21 | webPreferences: { 22 | contextIsolation: true, 23 | nodeIntegration: false, 24 | sandbox: true, 25 | }, 26 | }) 27 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/windows/Alert/preload.ts: -------------------------------------------------------------------------------- 1 | import { contextBridge, ipcRenderer } from "electron" 2 | 3 | function alertError(error: Error | string) { 4 | if (error instanceof Error) { 5 | error = error.message 6 | } 7 | 8 | ipcRenderer.send('alert-error', error) 9 | } 10 | 11 | function acceptAlert() { 12 | ipcRenderer.send('accept-alert') 13 | } 14 | 15 | contextBridge.exposeInMainWorld('acceptAlert', acceptAlert); 16 | contextBridge.exposeInMainWorld('alertError', alertError); 17 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/windows/Confirm/controller.ts: -------------------------------------------------------------------------------- 1 | import { WindowConfig } from 'browser/types' 2 | 3 | const DEFAULT_WIDTH = 400 4 | const DEFAULT_HEIGHT = 150 5 | 6 | export const window: WindowConfig['window'] = () => ({ 7 | width: DEFAULT_WIDTH, 8 | height: DEFAULT_HEIGHT, 9 | minWidth: DEFAULT_WIDTH, 10 | minHeight: DEFAULT_HEIGHT, 11 | resizable: false, 12 | minimizable: false, 13 | fullscreenable: false, 14 | autoHideMenuBar: true, 15 | maximizable: false, 16 | show: false, 17 | skipTaskbar: true, 18 | useContentSize: false, 19 | modal: true, 20 | title: 'Window Prompt Polyfill', 21 | webPreferences: { 22 | contextIsolation: true, 23 | nodeIntegration: false, 24 | sandbox: true, 25 | }, 26 | }) 27 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/windows/Confirm/preload.ts: -------------------------------------------------------------------------------- 1 | import { contextBridge, ipcRenderer } from "electron" 2 | 3 | function acceptConfirmation() { 4 | ipcRenderer.send('accept-confirmation', true) 5 | } 6 | 7 | function confirmationError(error: Error | string) { 8 | if (error instanceof Error) { 9 | error = error.message 10 | } 11 | 12 | ipcRenderer.send('confirmation-error', error) 13 | } 14 | 15 | function dismissConfirmation() { 16 | ipcRenderer.send('dismiss-confirmation', false) 17 | } 18 | 19 | contextBridge.exposeInMainWorld('dismissConfirmation', dismissConfirmation); 20 | contextBridge.exposeInMainWorld('confirmError', confirmationError); 21 | contextBridge.exposeInMainWorld('acceptConfirmation', acceptConfirmation); 22 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/windows/PlaybackWindow/controller.ts: -------------------------------------------------------------------------------- 1 | import { WindowConfig } from 'browser/types' 2 | 3 | export const window: WindowConfig['window'] = () => ({ 4 | frame: false, 5 | resizable: false, 6 | roundedCorners: false, 7 | show: false, 8 | skipTaskbar: true, 9 | title: 'Playback Window', 10 | }) 11 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/windows/PlaybackWindow/preload/__mocks__/utils.js: -------------------------------------------------------------------------------- 1 | // Licensed to the Software Freedom Conservancy (SFC) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The SFC licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | const isTest = true 19 | 20 | export { isTest } 21 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/windows/PlaybackWindow/renderer.tsx: -------------------------------------------------------------------------------- 1 | import AppWrapper from 'browser/components/AppWrapper' 2 | import ProjectPlaybackWindow from 'browser/components/PlaybackPanel' 3 | import renderWhenReady from 'browser/helpers/renderWhenReady' 4 | import React from 'react' 5 | 6 | const PlaybackWindowLanding = () => ( 7 | 8 | 9 | 10 | ) 11 | 12 | renderWhenReady(PlaybackWindowLanding) 13 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/windows/PlaybackWindowBidi/controller.ts: -------------------------------------------------------------------------------- 1 | import { WindowConfig } from 'browser/types' 2 | import Electron from 'electron' 3 | 4 | const dimensions = { 5 | height: 600, 6 | width: 800, 7 | } 8 | export const window: WindowConfig['window'] = () => { 9 | const display = Electron.screen.getPrimaryDisplay() 10 | return { 11 | ...dimensions, 12 | x: 13 | Math.floor(display.bounds.width / 2) - 14 | Math.floor(dimensions.width / 2) - 15 | 275, 16 | y: 17 | Math.floor(display.bounds.height / 2) - 18 | Math.floor(dimensions.height / 2) - 19 | 50, 20 | title: 'Playback Window', 21 | webPreferences: { 22 | nodeIntegrationInSubFrames: true, 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/windows/PlaybackWindowBidi/renderer.tsx: -------------------------------------------------------------------------------- 1 | import Paper from '@mui/material/Paper' 2 | import Typography from '@mui/material/Typography' 3 | import React from 'react' 4 | import AppWrapper from 'browser/components/AppWrapper' 5 | import renderWhenReady from 'browser/helpers/renderWhenReady' 6 | 7 | const ProjectPlaybackControls = () => ( 8 | 9 | 10 | 11 | This is where recording and playback will occur 12 | 13 | 14 | 15 | ) 16 | 17 | renderWhenReady(ProjectPlaybackControls) 18 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/windows/ProjectEditor/controller.ts: -------------------------------------------------------------------------------- 1 | import { WindowConfig } from 'browser/types' 2 | import Electron from 'electron' 3 | import { platform } from 'os' 4 | 5 | export const window: WindowConfig['window'] = () => { 6 | const display = Electron.screen.getPrimaryDisplay() 7 | return { 8 | x: 50, 9 | y: 50, 10 | width: display.bounds.width - 100, 11 | height: display.bounds.height - 100, 12 | titleBarStyle: platform() === 'darwin' ? 'hidden' : 'default', 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/windows/ProjectEditor/preload.ts: -------------------------------------------------------------------------------- 1 | import preload from '../../helpers/preload-electron' 2 | 3 | preload() 4 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/windows/ProjectEditor/tabs/Suites/Controls.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useContext } from 'react' 2 | import PauseButton from '../../../../components/Controls/PauseButton' 3 | import PlayListButton from '../../../../components/Controls/PlayListButton' 4 | import StopButton from '../../../../components/Controls/StopButton' 5 | import { context } from 'browser/contexts/status' 6 | 7 | const activeStates = ['recording', 'playing'] 8 | 9 | const SuiteControls: FC = () => { 10 | const status = useContext(context) 11 | return ( 12 | <> 13 | {activeStates.includes(status) ? ( 14 | <> 15 | 16 | 17 | 18 | ) : ( 19 | <> 20 | 21 | 22 | )} 23 | 24 | ) 25 | } 26 | 27 | export default SuiteControls 28 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/windows/ProjectEditor/tabs/Suites/SuitesTab.tsx: -------------------------------------------------------------------------------- 1 | import Box from '@mui/material/Box' 2 | import React, { useContext } from 'react' 3 | import SuiteEditor from './Editor' 4 | import SuiteViewer from './Viewer' 5 | import SuiteSelector from './SuiteSelector' 6 | import { context } from 'browser/contexts/session' 7 | 8 | const SuitesTab: React.FC = () => { 9 | const session = useContext(context) 10 | const Component = 11 | session.state.editor.suiteMode === 'editor' ? SuiteEditor : SuiteViewer 12 | 13 | return ( 14 | 15 | {!session.state.editor.showDrawer && } 16 | 17 | 18 | ) 19 | } 20 | 21 | export default SuitesTab 22 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/windows/ProjectEditor/tabs/Tests/CommandFields/utils.ts: -------------------------------------------------------------------------------- 1 | export const setField = 2 | (name: string) => (testID: string, commandID: string) => (value: T) => { 3 | window.sideAPI.tests.updateStep(testID, commandID, { 4 | [name]: value, 5 | }) 6 | } 7 | 8 | export const updateACField = 9 | (name: string) => 10 | (testID: string, commandID: string) => 11 | (_e: any, v: any) => { 12 | window.sideAPI.tests.updateStep(testID, commandID, { 13 | [name]: v.id, 14 | }) 15 | } 16 | 17 | export const updateField = 18 | (name: string) => (testID: string, commandID: string) => (e: any) => { 19 | window.sideAPI.tests.updateStep(testID, commandID, { 20 | [name]: e?.target?.value ?? "", 21 | }) 22 | } 23 | 24 | export const updateFieldAutoComplete = 25 | (name: string) => 26 | (testID: string, commandID: string) => 27 | (_e: any, value: string) => { 28 | window.sideAPI.tests.updateStep(testID, commandID, { 29 | [name]: value, 30 | }) 31 | } 32 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/windows/ProjectEditor/tabs/Tests/types.ts: -------------------------------------------------------------------------------- 1 | import { CommandShape } from '@seleniumhq/side-model' 2 | import { CoreSessionData, LocatorFields } from '@seleniumhq/side-api' 3 | 4 | export interface CommandEditorProps { 5 | command: CommandShape 6 | commands: CoreSessionData['state']['commands'] 7 | disabled?: boolean 8 | testID: string 9 | } 10 | 11 | export type CommandSelectorProps = CommandEditorProps & { 12 | isDisabled: boolean 13 | } 14 | 15 | export interface CommandArgFieldProps extends CommandEditorProps { 16 | fieldName: LocatorFields 17 | } 18 | 19 | export interface CommandFieldProps extends CommandEditorProps { 20 | fieldName: 'comment' | 'windowHandleName' | 'windowTimeout' | LocatorFields 21 | note?: string 22 | } 23 | 24 | export interface MiniCommandShape { 25 | id: string 26 | name: string 27 | } 28 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/windows/Prompt/controller.ts: -------------------------------------------------------------------------------- 1 | import { WindowConfig } from 'browser/types' 2 | 3 | const DEFAULT_WIDTH = 500 4 | const DEFAULT_HEIGHT = 200 5 | 6 | export const window: WindowConfig['window'] = () => ({ 7 | width: DEFAULT_WIDTH, 8 | height: DEFAULT_HEIGHT, 9 | resizable: false, 10 | minimizable: false, 11 | fullscreenable: false, 12 | autoHideMenuBar: true, 13 | frame: false, 14 | maximizable: false, 15 | show: false, 16 | skipTaskbar: true, 17 | useContentSize: false, 18 | title: 'Window Prompt Polyfill', 19 | webPreferences: { 20 | contextIsolation: true, 21 | nodeIntegration: false, 22 | sandbox: true, 23 | }, 24 | }) 25 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/windows/Prompt/preload.ts: -------------------------------------------------------------------------------- 1 | import { contextBridge, ipcRenderer } from "electron" 2 | 3 | function dismissPrompt() { 4 | ipcRenderer.send('dismiss-prompt') 5 | } 6 | 7 | function promptError(error: Error | string) { 8 | if (error instanceof Error) { 9 | error = error.message 10 | } 11 | 12 | ipcRenderer.send('prompt-error', error) 13 | } 14 | 15 | function answerPrompt(answer: string) { 16 | ipcRenderer.send('answer-prompt', answer) 17 | } 18 | 19 | contextBridge.exposeInMainWorld('answerPrompt', answerPrompt); 20 | contextBridge.exposeInMainWorld('dismissPrompt', dismissPrompt); 21 | contextBridge.exposeInMainWorld('promptError', promptError); 22 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/windows/Splash/controller.ts: -------------------------------------------------------------------------------- 1 | import { WindowConfig } from 'browser/types' 2 | 3 | export const window: WindowConfig['window'] = () => ({ 4 | width: 600, 5 | height: 400, 6 | title: 'Selenium IDE - Splash', 7 | }) 8 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/windows/Splash/preload.ts: -------------------------------------------------------------------------------- 1 | import preload from '../../helpers/preload-electron' 2 | 3 | preload() 4 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/windows/UpdateNotifier/controller.ts: -------------------------------------------------------------------------------- 1 | import { WindowConfig } from 'browser/types' 2 | import Electron from 'electron' 3 | 4 | const WIDTH = 300 5 | const HEIGHT = 120 6 | 7 | export const window: WindowConfig['window'] = () => { 8 | const display = Electron.screen.getPrimaryDisplay() 9 | return { 10 | x: display.bounds.width - WIDTH - 50, 11 | y: display.bounds.height - HEIGHT - 50, 12 | width: WIDTH, 13 | height: HEIGHT, 14 | resizable: false, 15 | minimizable: false, 16 | fullscreenable: false, 17 | autoHideMenuBar: true, 18 | frame: false, 19 | maximizable: false, 20 | skipTaskbar: true, 21 | useContentSize: false, 22 | title: 'Checking for updates', 23 | webPreferences: { 24 | contextIsolation: true, 25 | nodeIntegration: false, 26 | sandbox: true, 27 | }, 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/windows/UpdateNotifier/preload.ts: -------------------------------------------------------------------------------- 1 | import { contextBridge, ipcRenderer } from "electron" 2 | 3 | function doRestart() { 4 | ipcRenderer.send('do-restart ') 5 | } 6 | 7 | contextBridge.exposeInMainWorld('doRestart', doRestart); 8 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/browser/windows/controller.ts: -------------------------------------------------------------------------------- 1 | export * as Alert from './Alert/controller' 2 | export * as Confirm from './Confirm/controller' 3 | export * as PlaybackWindow from './PlaybackWindow/controller' 4 | export * as ProjectEditor from './ProjectEditor/controller' 5 | export * as Prompt from './Prompt/controller' 6 | export * as Splash from './Splash/controller' 7 | export * as UpdateNotifier from './UpdateNotifier/controller' 8 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/main/api/helpers/getCore.ts: -------------------------------------------------------------------------------- 1 | import { CoreSessionData } from '@seleniumhq/side-api' 2 | import { Session } from 'main/types' 3 | 4 | export default (session: Session): CoreSessionData => ({ 5 | project: session.projects.project, 6 | state: session.state.state, 7 | }) 8 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/main/constants/preloadScriptPath.ts: -------------------------------------------------------------------------------- 1 | import path from 'node:path' 2 | 3 | export default path.join(__dirname, 'preload-bundle.js') 4 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/main/constants/rendererPath.ts: -------------------------------------------------------------------------------- 1 | import path from 'node:path' 2 | 3 | export default path.join(__dirname, '..', 'public', 'index.html') 4 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/main/install-react-devtools.ts: -------------------------------------------------------------------------------- 1 | import installExtension, { 2 | REACT_DEVELOPER_TOOLS, 3 | } from 'electron-devtools-installer' 4 | 5 | export default () => { 6 | installExtension(REACT_DEVELOPER_TOOLS, { 7 | loadExtensionOptions: { allowFileAccess: true }, 8 | }) 9 | .then((name) => console.log(`Added Extension: ${name}`)) 10 | .catch((err) => console.error('An error occurred: ', err)) 11 | } 12 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/main/session/controllers/ArgTypes/index.ts: -------------------------------------------------------------------------------- 1 | import ArgTypes from '@seleniumhq/side-model/dist/ArgTypes' 2 | import BaseController from '../Base' 3 | 4 | /** 5 | * This API is meant to be responsible for maintaining the state 6 | * of the argument types list in the main process 7 | */ 8 | export default class ArgTypesController extends BaseController { 9 | async get() { 10 | return ArgTypes 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/main/session/controllers/Base/index.ts: -------------------------------------------------------------------------------- 1 | import { Session } from 'main/types' 2 | 3 | /** 4 | * This is the base controller instance, containing our lowest level hooks 5 | * and identifying the objects downstream as a controller. 6 | */ 7 | export default class BaseController { 8 | constructor(session: Session) { 9 | this.session = session 10 | } 11 | async onProjectLoaded() {} 12 | async onProjectUnloaded() {} 13 | isController = true 14 | priority: number = 0 15 | session: Session 16 | } 17 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/main/session/controllers/Channels/index.ts: -------------------------------------------------------------------------------- 1 | import BaseController from '../Base' 2 | 3 | /** 4 | * This holds on to all commands currently in the command map. 5 | * It's primarily instrumented by loading plugins 6 | */ 7 | export default class ChannelsController extends BaseController { 8 | async send(channel: string, ...args: any[]) { 9 | await this.session.api.channels.onSend.dispatchEvent(channel, ...args) 10 | } 11 | // This needs to build after plugins 12 | priority = 5 13 | } 14 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/main/session/controllers/Menu/menus/help.ts: -------------------------------------------------------------------------------- 1 | import { MenuComponent } from 'main/types' 2 | import { menuFactoryFromCommandFactory } from '../utils' 3 | 4 | export const commands: MenuComponent = (session) => () => 5 | [ 6 | { 7 | accelerator: 'CommandOrControl+Shift+D', 8 | click: async () => { 9 | await session.system.dumpSession() 10 | }, 11 | label: session.system.languageMap.helpMenuTree.dumpSession, 12 | }, 13 | ] 14 | 15 | export default menuFactoryFromCommandFactory(commands) 16 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/main/session/controllers/Menu/menus/textField.ts: -------------------------------------------------------------------------------- 1 | import { menuFactoryFromCommandFactory } from '../utils' 2 | import { MenuComponent } from 'main/types' 3 | 4 | export const commands: MenuComponent = () => () => 5 | [ 6 | { 7 | accelerator: 'CommandOrControl+X', 8 | label: 'Cut', 9 | role: 'cut', 10 | }, 11 | { 12 | accelerator: 'CommandOrControl+C', 13 | label: 'Copy', 14 | role: 'copy', 15 | }, 16 | { 17 | accelerator: 'CommandOrControl+V', 18 | label: 'Paste', 19 | role: 'paste', 20 | }, 21 | { 22 | type: 'separator', 23 | }, 24 | ] 25 | 26 | export default menuFactoryFromCommandFactory(commands) 27 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/main/session/controllers/Menu/utils.ts: -------------------------------------------------------------------------------- 1 | import { Menu, MenuItemConstructorOptions } from 'electron' 2 | import { Session } from 'main/types' 3 | 4 | export const multipleCommand = ( 5 | accelerators: string[], 6 | options: Omit 7 | ): MenuItemConstructorOptions[] => 8 | accelerators.map((accelerator, index) => ({ 9 | ...options, 10 | accelerator, 11 | visible: index === 0, 12 | })) 13 | 14 | export const menuFactoryFromCommandFactory = 15 | ( 16 | commands: ( 17 | session: Session 18 | ) => (...args: any[]) => MenuItemConstructorOptions[] 19 | ) => 20 | (session: Session) => 21 | async (...args: any[]) => 22 | Menu.buildFromTemplate(commands(session)(...args)) 23 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/main/session/controllers/Suites/index.ts: -------------------------------------------------------------------------------- 1 | import { SuiteShape } from '@seleniumhq/side-model' 2 | import { randomUUID } from 'crypto' 3 | import BaseController from '../Base' 4 | 5 | export default class SuitesController extends BaseController { 6 | async create(name?: string): Promise { 7 | return { 8 | id: randomUUID(), 9 | name: name === undefined ? 'New Suite' : name, 10 | persistSession: false, 11 | parallel: false, 12 | tests: [], 13 | timeout: 30000, 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/selenium-ide/src/main/store/config.ts: -------------------------------------------------------------------------------- 1 | export default {} 2 | -------------------------------------------------------------------------------- /packages/side-api/src/classes/Constant.ts: -------------------------------------------------------------------------------- 1 | export type ConstantShape = (val: T) => () => T 2 | 3 | const Constant: ConstantShape = (value) => () => value 4 | 5 | export default Constant 6 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/channels/index.ts: -------------------------------------------------------------------------------- 1 | import type { Shape as OnSend } from './onSend' 2 | import type { Shape as Send } from './send' 3 | 4 | import * as onSend from './onSend' 5 | import * as send from './send' 6 | 7 | export const commands = { 8 | onSend, 9 | send, 10 | } 11 | 12 | /** 13 | * Manages the establishing of bidirectional communication channels 14 | */ 15 | export type Shape = { 16 | onSend: OnSend 17 | send: Send 18 | } 19 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/channels/onSend.ts: -------------------------------------------------------------------------------- 1 | import { BaseListener } from '../../types/base' 2 | 3 | /** 4 | * Contains a channel UUID and variadic arguments 5 | */ 6 | export type Shape = BaseListener<[string, ...any[]]> 7 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/channels/send.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Emit messages along a channel 3 | */ 4 | export type Shape = (channel: string, ...args: any[]) => Promise 5 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/dialogs/confirm.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Shows a confirm dialog and passes back the boolean of confirmation 3 | */ 4 | export type Shape = () => Promise 5 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/dialogs/index.ts: -------------------------------------------------------------------------------- 1 | import type { Shape as Confirm } from './confirm' 2 | import type { Shape as Open } from './open' 3 | 4 | import * as confirm from './confirm' 5 | import * as open from './open' 6 | 7 | export const commands = { 8 | confirm, 9 | open, 10 | } 11 | 12 | /** 13 | * Manages the presenting of dialogs to the user 14 | */ 15 | export type Shape = { 16 | confirm: Confirm 17 | open: Open 18 | } 19 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/dialogs/open.ts: -------------------------------------------------------------------------------- 1 | import type { OpenDialogReturnValue } from 'electron' 2 | /** 3 | * Shows an open file dialog and passes back the async result 4 | */ 5 | export type Shape = () => Promise 6 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/driver/download.ts: -------------------------------------------------------------------------------- 1 | import { BrowserInfo } from '../../types/base' 2 | 3 | /** 4 | * Download a driver needed to instrument an available browser. 5 | */ 6 | export type Shape = (info: BrowserInfo) => Promise 7 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/driver/listBrowsers.ts: -------------------------------------------------------------------------------- 1 | import { BrowsersInfo } from '../../types/base' 2 | 3 | /** 4 | * List available browsers to be driven. 5 | */ 6 | export type Shape = () => Promise 7 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/driver/selectBrowser.ts: -------------------------------------------------------------------------------- 1 | import { BrowserInfo } from '../../types/base' 2 | 3 | /** 4 | * Select a browser to run. Currently only Electron is allowed. 5 | */ 6 | export type Shape = (selected: BrowserInfo) => Promise 7 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/driver/startProcess.ts: -------------------------------------------------------------------------------- 1 | import { BrowserInfo } from '../../types/base' 2 | 3 | /** 4 | * Start the driver process for the browser, eg Chromedriver etc.. 5 | */ 6 | export type Shape = (info: BrowserInfo) => Promise 7 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/driver/stopProcess.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Stop the driver process for the browser 3 | */ 4 | export type Shape = () => Promise 5 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/driver/takeScreenshot.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Return a base64 encoded screenshot of the current window. 3 | */ 4 | export type Shape = () => Promise 5 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/menus/index.ts: -------------------------------------------------------------------------------- 1 | import type { Shape as Open } from './open' 2 | import type { Shape as OpenSync } from './openSync' 3 | 4 | import * as open from './open' 5 | import * as openSync from './openSync' 6 | 7 | export const commands = { 8 | open, 9 | openSync, 10 | } 11 | 12 | /** 13 | * API for dealing with menus. Of course, only real interaction is popping them 14 | * up right now 15 | */ 16 | export type Shape = { 17 | open: Open 18 | openSync: OpenSync 19 | } 20 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/menus/open.ts: -------------------------------------------------------------------------------- 1 | export type Type = 'Handler' 2 | export type MenuNames = 3 | | 'application' 4 | | 'editBasics' 5 | | 'playback' 6 | | 'projectEditor' 7 | | 'suiteManager' 8 | | 'testEditor' 9 | | 'projectView' 10 | | 'testManager' 11 | | 'textField' 12 | /** 13 | * Opens one of our available menu types. Types are governed 14 | * by Selenium IDE internals (sorry, no custom menus -_-) 15 | */ 16 | export type Shape = (name: MenuNames, ...args: any[]) => Promise 17 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/menus/openSync.ts: -------------------------------------------------------------------------------- 1 | import { MenuNames } from './open' 2 | 3 | export type Type = 'Handler' 4 | /** 5 | * Opens one of our available menu types, and returns the name of the selected option. 6 | */ 7 | export type Shape = (name: MenuNames, ...args: any[]) => Promise 8 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/playback/onAfter.ts: -------------------------------------------------------------------------------- 1 | import { BaseListener } from '../../types/base' 2 | 3 | /** 4 | * Runs after a test completes 5 | */ 6 | export type OnAfterPlayback = [ 7 | { 8 | suite: string 9 | test: string 10 | endIndex: number 11 | status: 'success' | 'failure' 12 | error?: string 13 | } 14 | ] 15 | export type Shape = BaseListener 16 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/playback/onAfterAll.ts: -------------------------------------------------------------------------------- 1 | import { BaseListener } from '../../types/base' 2 | 3 | /** 4 | * Runs after a suite completes 5 | */ 6 | export type OnAfterAllPlayback = [ 7 | { 8 | suite: string 9 | } 10 | ] 11 | export type Shape = BaseListener 12 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/playback/onBefore.ts: -------------------------------------------------------------------------------- 1 | import { BaseListener } from '../../types/base' 2 | 3 | /** 4 | * Runs before a test begins 5 | */ 6 | export type OnBeforePlayback = [ 7 | { 8 | suite: string 9 | test: string 10 | startIndex: number 11 | endIndex: number 12 | } 13 | ] 14 | export type Shape = BaseListener 15 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/playback/onBeforeAll.ts: -------------------------------------------------------------------------------- 1 | import { BaseListener } from '../../types/base' 2 | 3 | export type Type = 'EventListener' 4 | 5 | /** 6 | * Runs before a suite begins 7 | */ 8 | export type OnBeforeAllPlayback = [ 9 | { 10 | suite: string 11 | } 12 | ] 13 | export type Shape = BaseListener 14 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/playback/pause.ts: -------------------------------------------------------------------------------- 1 | import set from 'lodash/fp/set' 2 | import { Mutator } from '../../types/base' 3 | 4 | /** 5 | * Pause a running test. Can be resumed. 6 | */ 7 | export type Shape = () => Promise 8 | 9 | const setToPaused = set('state.status', 'paused') 10 | export const mutator: Mutator = (session) => setToPaused(session) 11 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/playback/performCommand.ts: -------------------------------------------------------------------------------- 1 | import { CommandShape } from '@seleniumhq/side-model' 2 | 3 | /** 4 | * Attempts to execute a command supplied via API directly 5 | */ 6 | export type Shape = (cmd: Omit) => Promise 7 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/playback/playSuite.ts: -------------------------------------------------------------------------------- 1 | import set from 'lodash/fp/set' 2 | import update from 'lodash/fp/update' 3 | import { getActiveSuite } from '../../helpers' 4 | import { TestResultShape } from '../../models' 5 | import { Mutator } from '../../types/base' 6 | 7 | /** 8 | * Start running a test suite. Results should be processed using 9 | * onStepUpdate and onPlayUpdate. 10 | */ 11 | export type Shape = () => Promise 12 | 13 | export const mutator: Mutator = (session) => 14 | update( 15 | 'state.playback.testResults', 16 | (testResults: Record) => { 17 | const { tests } = getActiveSuite(session) 18 | return Object.fromEntries( 19 | Object.entries(testResults).filter(([key]) => tests.indexOf(key) === -1) 20 | ) 21 | }, 22 | set('state.status', 'playing', session) 23 | ) 24 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/playback/resume.ts: -------------------------------------------------------------------------------- 1 | import set from 'lodash/fp/set' 2 | import { Mutator } from '../../types/base' 3 | 4 | /** 5 | * Resume running a paused test 6 | */ 7 | export type Shape = () => Promise 8 | 9 | const setToPlay = set('state.status', 'playing') 10 | export const mutator: Mutator = (session) => setToPlay(session) 11 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/playback/stop.ts: -------------------------------------------------------------------------------- 1 | import set from 'lodash/fp/set' 2 | import { badIndex } from '../../constants' 3 | import { Mutator } from '../../types/base' 4 | 5 | /** 6 | * Abort a test. Can not be resumed 7 | */ 8 | export type Shape = () => Promise 9 | 10 | const setPlaybackToBadIndex = set('state.playback.currentIndex', badIndex) 11 | const setToIdle = set('state.status', 'idle') 12 | export const mutator: Mutator = (session) => 13 | setPlaybackToBadIndex(setToIdle(session)) 14 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/plugins/addPreloadScript.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This api call only works from a preload script, and is used to 3 | * register middleware to modify recorded commands 4 | * 5 | * Script is the javascript to be executed in the browsing context. 6 | * 7 | * Sandbox is whether the browsing context is the parent(false) or 8 | * the recorder context(true, default). 9 | */ 10 | export type Shape = (script: string, sandbox?: boolean) => void 11 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/plugins/addRecorderPreprocessor.ts: -------------------------------------------------------------------------------- 1 | import { RecorderPreprocessor } from '../../types/plugin' 2 | 3 | /** 4 | * This api call only works from a sanboxed preload script, and is used to 5 | * register middleware to modify recorded commands 6 | */ 7 | export type Shape = (preprocessor: RecorderPreprocessor) => void 8 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/plugins/getPreloads.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Get the active plugin preload scripts 3 | */ 4 | export type Shape = () => Promise 5 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/plugins/list.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Get a list of active plugins 3 | */ 4 | export type Shape = () => Promise 5 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/plugins/listPreloadPaths.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Get a list of active plugin preload paths 3 | */ 4 | export type Shape = () => Promise 5 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/plugins/onRequestCustomEditorPanel.ts: -------------------------------------------------------------------------------- 1 | import { BaseListener } from '../../types/base' 2 | 3 | export type Type = 'EventListener' 4 | 5 | /** 6 | * Emitted to the frontend when requested from the backend 7 | */ 8 | export type OnRequestCustomEditorPanel = [name: string, url: string] 9 | export type Shape = BaseListener 10 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/plugins/projectCreate.ts: -------------------------------------------------------------------------------- 1 | import update from 'lodash/fp/update' 2 | import { Mutator } from '../../types/base' 3 | 4 | /** 5 | * Add a plugin to the current project 6 | */ 7 | export type Shape = () => Promise 8 | 9 | export const mutator: Mutator = (session) => 10 | update('project.plugins', (plugins: string[]) => plugins.concat(''), session) 11 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/plugins/projectDelete.ts: -------------------------------------------------------------------------------- 1 | import update from 'lodash/fp/update' 2 | import { Mutator } from '../../types/base' 3 | 4 | /** 5 | * Remove a plugin from the current project 6 | */ 7 | export type Shape = (index: number) => Promise 8 | 9 | export const mutator: Mutator = (session, { params: [index] }) => 10 | update( 11 | 'project.plugins', 12 | (plugins: string[]) => { 13 | const newPlugins = plugins.slice(0) 14 | newPlugins.splice(index, 1) 15 | return newPlugins 16 | }, 17 | session 18 | ) 19 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/plugins/projectEdit.ts: -------------------------------------------------------------------------------- 1 | import update from 'lodash/fp/update' 2 | import { Mutator } from '../../types/base' 3 | 4 | /** 5 | * Update the path to a plugin on the current project 6 | */ 7 | export type Shape = (index: number, path: string) => Promise 8 | 9 | export const mutator: Mutator = (session, { params: [index, path] }) => 10 | update( 11 | 'project.plugins', 12 | (plugins: string[]) => { 13 | const newPlugins = plugins.slice(0) 14 | newPlugins[index] = path 15 | return newPlugins 16 | }, 17 | session 18 | ) 19 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/projects/getActive.ts: -------------------------------------------------------------------------------- 1 | import { ProjectShape } from '@seleniumhq/side-model' 2 | 3 | /** 4 | * Returns the current project being edited 5 | */ 6 | export type Shape = () => Promise 7 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/projects/getRecent.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Returns a list of paths to recent projects 3 | */ 4 | export type Shape = () => Promise 5 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/projects/load.ts: -------------------------------------------------------------------------------- 1 | import { loadingID } from '../../constants/loadingID' 2 | import { CoreSessionData, Mutator } from '../../types/base' 3 | 4 | /** 5 | * Loads the project editor for a path 6 | */ 7 | export type Shape = (filepath: string) => Promise 8 | 9 | export const mutator: Mutator = (session, { result }) => { 10 | if (!result) { 11 | return session 12 | } 13 | // result.state = defaultState 14 | const { project, state } = result 15 | if (state) { 16 | return result 17 | } 18 | const activeTestID = project.tests[0]?.id ?? loadingID 19 | return { 20 | project, 21 | state: { 22 | ...session.state, 23 | activeSuiteID: '', 24 | activeTestID, 25 | }, 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/projects/new.ts: -------------------------------------------------------------------------------- 1 | import { ProjectShape } from '@seleniumhq/side-model' 2 | import { Mutator } from '../../types/base' 3 | 4 | /** 5 | * Creates a new project 6 | */ 7 | export type Shape = () => Promise 8 | 9 | export const mutator: Mutator = (session, { result }) => ({ 10 | ...session, 11 | state: { 12 | ...session.state, 13 | activeTestID: result.tests[0].id, 14 | editor: { 15 | ...session.state.editor, 16 | selectedCommandIndexes: [0], 17 | }, 18 | }, 19 | project: result, 20 | }) 21 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/projects/save.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Saves the current project to a path 3 | */ 4 | export type Shape = (filepath: string) => Promise 5 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/projects/select.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Used during initialization to load a .side file by double clicking it. 3 | */ 4 | export type Shape = () => Promise 5 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/projects/update.ts: -------------------------------------------------------------------------------- 1 | import { ProjectShape } from '@seleniumhq/side-model' 2 | import merge from 'lodash/fp/merge' 3 | import update from 'lodash/fp/update' 4 | import { CoreSessionData, Mutator } from '../../types/base' 5 | 6 | /** 7 | * Edits project level config flags, like name or url. 8 | */ 9 | export type Shape = ( 10 | updates: Partial> 11 | ) => Promise 12 | 13 | export const mutator: Mutator = ( 14 | session: CoreSessionData, 15 | { params: [updates] } 16 | ) => 17 | update(`project`, (project: ProjectShape) => merge(project, updates), session) 18 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/recorder/getFrameLocation.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Used by the main process to construct the frame tree 3 | */ 4 | export type Shape = () => Promise 5 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/recorder/getLocatorOrder.ts: -------------------------------------------------------------------------------- 1 | export type Shape = () => Promise 2 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/recorder/getWinHandleId.ts: -------------------------------------------------------------------------------- 1 | export type Shape = () => Promise 2 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/recorder/onFrameDeleted.ts: -------------------------------------------------------------------------------- 1 | import { BaseListener } from '../../types/base' 2 | 3 | export type OnFrameDeletedRecorder = [] 4 | 5 | /** 6 | * Called when a frame is deleted 7 | */ 8 | export type Shape = BaseListener 9 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/recorder/onFrameRecalculate.ts: -------------------------------------------------------------------------------- 1 | import { BaseListener } from '../../types/base' 2 | 3 | export type OnFrameRecalculateRecorder = [] 4 | 5 | /** 6 | * Called when a frame is added, to force the frame tree 7 | * to reflow itself 8 | */ 9 | export type Shape = BaseListener 10 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/recorder/onHighlightElement.ts: -------------------------------------------------------------------------------- 1 | import { BaseListener } from '../../types/base' 2 | 3 | /** 4 | * Called when the project editor asks the recorder to highlight an element 5 | * on the playback page. 6 | */ 7 | export type Shape = BaseListener 8 | export type OnHighlightElementRecorder = [string] 9 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/recorder/onLocatorOrderChanged.ts: -------------------------------------------------------------------------------- 1 | import { BaseListener, EventMutator } from '../../types/base' 2 | 3 | export type OnLocatorOrderChanged = [string[]] 4 | 5 | /** 6 | * Runs whenever the order of locators is changed 7 | */ 8 | export type Shape = BaseListener 9 | 10 | export const mutator: EventMutator = ( 11 | session, 12 | [locators] 13 | ) => ({ 14 | ...session, 15 | state: { 16 | ...session.state, 17 | locators, 18 | }, 19 | }) 20 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/recorder/onRequestElementAt.ts: -------------------------------------------------------------------------------- 1 | import { BaseListener } from '../../types/base' 2 | 3 | /** 4 | * Called when the project editor asks the recorder to select an element. 5 | */ 6 | export type Shape = BaseListener< 7 | OnRequestElementAtRecorder, 8 | OnRequestElementAtRecorderResult 9 | > 10 | export type OnRequestElementAtRecorder = [number, number] 11 | export type OnRequestElementAtRecorderResult = [string, string][] | null 12 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/recorder/onRequestSelectElement.ts: -------------------------------------------------------------------------------- 1 | import { BaseListener, LocatorFields } from '../../types/base' 2 | 3 | /** 4 | * Called when the project editor asks the recorder to select an element. 5 | */ 6 | export type Shape = BaseListener 7 | export type OnRequestSelectElementRecorder = [boolean, LocatorFields] 8 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/recorder/requestElementAt.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Return a list of targets that are at the given coordinates. 3 | */ 4 | export type Shape = (x: number, y: number) => Promise<[string, string][]> 5 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/recorder/requestHighlightElement.ts: -------------------------------------------------------------------------------- 1 | import { LocatorFields } from '../../types/base' 2 | 3 | /** 4 | * Asks the playback windows to highlight the current locator. 5 | */ 6 | export type Shape = (fieldName: LocatorFields) => Promise 7 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/recorder/requestSelectElement.ts: -------------------------------------------------------------------------------- 1 | import { LocatorFields } from '../../types/base' 2 | 3 | /** 4 | * Asks the playback windows to set the next element clicked as the currently 5 | * selected locator field element. 6 | */ 7 | export type Shape = ( 8 | activate: boolean, 9 | fieldName: LocatorFields 10 | ) => Promise 11 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/recorder/selectElement.ts: -------------------------------------------------------------------------------- 1 | import capitalize from 'lodash/fp/capitalize' 2 | import { mutator as updateStepMutator } from '../tests/updateStep' 3 | import { getActiveCommand } from '../../helpers/getActiveData' 4 | import { LocatorFields, Mutator } from '../../types/base' 5 | 6 | /** 7 | * Tells the project editor what the requested select element was so the IDE 8 | * can assign it to the expected field. 9 | */ 10 | export type Shape = ( 11 | field: LocatorFields, 12 | element: [string, string][] 13 | ) => Promise 14 | 15 | export const mutator: Mutator = (session, { params }) => 16 | updateStepMutator(session, { 17 | params: [ 18 | session.state.activeTestID, 19 | getActiveCommand(session).id, 20 | { 21 | [params[0]]: params[1][0][0], 22 | [`fallback${capitalize(params[0])}s`]: params[1], 23 | }, 24 | ], 25 | result: undefined, 26 | }) 27 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/recorder/setWindowHandle.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * I am not fully sure what this does. This may be able to go. 3 | */ 4 | export type Shape = (sessionID: string, handle: string) => Promise 5 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/recorder/stop.ts: -------------------------------------------------------------------------------- 1 | import set from 'lodash/fp/set' 2 | import { Mutator } from '../../types/base' 3 | 4 | /** 5 | * Stop recording interactions across playback windows 6 | */ 7 | export type Shape = () => Promise 8 | 9 | const setToIdle = set('state.status', 'idle') 10 | export const mutator: Mutator = (session) => setToIdle(session) 11 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/resizable-panels/get-panel-group.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Get dimensions from user panels 3 | */ 4 | export type Shape = (panelID: string) => Promise 5 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/resizable-panels/index.ts: -------------------------------------------------------------------------------- 1 | import type { Shape as GetPanelGroup } from './get-panel-group' 2 | import type { Shape as SetPanelGroup } from './set-panel-group' 3 | 4 | import * as getPanelGroup from './get-panel-group' 5 | import * as setPanelGroup from './set-panel-group' 6 | 7 | export const commands = { 8 | getPanelGroup, 9 | setPanelGroup, 10 | } 11 | 12 | /** 13 | * API for resizing panels and persisting that state 14 | */ 15 | export type Shape = { 16 | getPanelGroup: GetPanelGroup 17 | setPanelGroup: SetPanelGroup 18 | } 19 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/resizable-panels/set-panel-group.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Set dimensions of user panels 3 | */ 4 | export type Shape = (panelID: string, dims: number[]) => Promise 5 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/state/closeTestEditor.ts: -------------------------------------------------------------------------------- 1 | import { EditorStateShape } from '../../models/state' 2 | 3 | /** 4 | * Removes selected command indexes. Called when test editor closes. 5 | */ 6 | export type Shape = () => Promise 7 | export type EditorUpdates = Pick 8 | export type StateUpdates = { editor: EditorUpdates } 9 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/state/get.ts: -------------------------------------------------------------------------------- 1 | import { CoreSessionData } from '../../types/base' 2 | 3 | /** 4 | * Get current session data. 5 | */ 6 | export type Shape = () => Promise 7 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/state/getUserPrefs.ts: -------------------------------------------------------------------------------- 1 | import { UserPrefs } from '../../models' 2 | 3 | /** 4 | * Get user preferences 5 | */ 6 | export type Shape = () => Promise 7 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/state/onMutate.ts: -------------------------------------------------------------------------------- 1 | import { BaseListener } from '../../types/base' 2 | 3 | export type OnMutate = [path: string, data: any] 4 | 5 | /** 6 | * Called whenever session state is manipulated by an api endpoint 7 | * or otherwise. 8 | */ 9 | export type Shape = BaseListener 10 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/state/openTestEditor.ts: -------------------------------------------------------------------------------- 1 | import { EditorStateShape } from '../../models/state' 2 | 3 | export type Shape = () => Promise 4 | /** 5 | * Called whenever test editor opens. Assigns command indexes 6 | */ 7 | export type EditorUpdates = Pick 8 | export type StateUpdates = { editor: EditorUpdates } 9 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/state/redo.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * De-undo 3 | */ 4 | export type Shape = () => Promise 5 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/state/set.ts: -------------------------------------------------------------------------------- 1 | import set from 'lodash/fp/set' 2 | import { Mutator } from '../../types/base' 3 | 4 | /** 5 | * Sets a key on the session state 6 | */ 7 | export type Shape = (path: string, value: any) => Promise 8 | 9 | export const mutator: Mutator = ( 10 | session, 11 | { params: [path, value] } 12 | ) => ({ 13 | ...session, 14 | state: set(path, value, session.state), 15 | }) 16 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/state/setActiveCommand.ts: -------------------------------------------------------------------------------- 1 | import merge from 'lodash/fp/merge' 2 | import { Mutator } from '../../types/base' 3 | import { EditorStateShape, PlaybackStateShape } from '../../models/state' 4 | import { getCommandIndex } from '../../helpers/getActiveData' 5 | 6 | /** 7 | * Sets the active command for the test editor 8 | */ 9 | export type Shape = (commandID: string) => Promise 10 | 11 | export type StateUpdates = { 12 | editor: Pick 13 | playback: Pick 14 | } 15 | 16 | export const mutator: Mutator = ( 17 | session, 18 | { params: [activeCommandID] } 19 | ) => { 20 | const index = getCommandIndex(session, activeCommandID) 21 | const stateUpdates: StateUpdates = { 22 | editor: { 23 | selectedCommandIndexes: [index], 24 | }, 25 | playback: { 26 | currentIndex: index, 27 | }, 28 | } 29 | return merge(session, { state: stateUpdates }) 30 | } 31 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/state/setActiveTest.ts: -------------------------------------------------------------------------------- 1 | import { Mutator } from '../../types/base' 2 | 3 | /** 4 | * Sets the active test for the test editor 5 | */ 6 | export type Shape = (testID: string) => Promise 7 | 8 | export const mutator: Mutator = ( 9 | session, 10 | { params: [activeTestID] } 11 | ) => ({ 12 | ...session, 13 | state: { 14 | ...session.state, 15 | activeTestID, 16 | editor: { 17 | ...session.state.editor, 18 | selectedCommandIndexes: [0], 19 | }, 20 | }, 21 | }) 22 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/state/setAll.ts: -------------------------------------------------------------------------------- 1 | import { CoreSessionData, Mutator } from '../../types/base' 2 | 3 | /** 4 | * Set the whole session state 5 | */ 6 | export type Shape = (state: CoreSessionData) => Promise 7 | 8 | export const mutator: Mutator = (_session, { params: [newSession] }) => 9 | newSession 10 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/state/setCopiedCommands.ts: -------------------------------------------------------------------------------- 1 | import omit from 'lodash/fp/omit' 2 | import set from 'lodash/fp/set' 3 | import { Mutator } from '../../types/base' 4 | import { getActiveTest } from '../../helpers/getActiveData' 5 | import { CommandShape } from '@seleniumhq/side-model' 6 | 7 | /** 8 | * Copy commands from the selected commands list 9 | */ 10 | export type Shape = () => Promise 11 | 12 | export const mutator: Mutator = (session) => { 13 | const selection = session.state.editor.selectedCommandIndexes 14 | const activeTest = getActiveTest(session) 15 | const selectedCommandIndexes: CommandShape[] = activeTest.commands.filter( 16 | (_cmd, index) => selection.includes(index) 17 | ) 18 | const copyFriendlyCommands = selectedCommandIndexes.map(omit(['id'])) 19 | return set('state.editor.copiedCommands', copyFriendlyCommands, session) 20 | } 21 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/state/toggleBreakpoint.ts: -------------------------------------------------------------------------------- 1 | import update from 'lodash/fp/update' 2 | import { CoreSessionData, Mutator } from '../../types/base' 3 | 4 | /** 5 | * Toggles breakpoints in the active test 6 | */ 7 | export type Shape = (stepID: string) => Promise 8 | 9 | export const mutator: Mutator = (session, { params: [commandID] }) => 10 | update( 11 | 'state.breakpoints', 12 | (breakpoints: CoreSessionData['state']['breakpoints']) => 13 | breakpoints.includes(commandID) 14 | ? breakpoints.filter((id) => id !== commandID) 15 | : breakpoints.concat(commandID), 16 | session 17 | ) 18 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/state/toggleSuiteMode.ts: -------------------------------------------------------------------------------- 1 | import set from 'lodash/fp/set' 2 | import { EditorStateShape } from '../../models' 3 | import { Mutator } from '../../types/base' 4 | 5 | /** 6 | * Toggles breakpoints in the active test 7 | */ 8 | export type Shape = (suiteMode: EditorStateShape['suiteMode']) => Promise 9 | 10 | export const mutator: Mutator = (session, { params: [suiteMode] }) => 11 | set('state.editor.suiteMode', suiteMode, session) 12 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/state/toggleUserPrefCamelCase.ts: -------------------------------------------------------------------------------- 1 | import set from 'lodash/fp/set' 2 | import { CamelCaseNamesPref } from '../../models/state' 3 | import { Mutator } from '../../types/base' 4 | 5 | /** 6 | * Customizes command insert behavior to either follow or lead the current 7 | * command 8 | */ 9 | export type Shape = (camelCaseNamesPref: CamelCaseNamesPref) => Promise 10 | 11 | export const mutator: Mutator = ( 12 | session, 13 | { params: [camelCaseNamesPref] } 14 | ) => set('state.userPrefs.camelCaseNamesPref', camelCaseNamesPref, session) 15 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/state/toggleUserPrefDisableCodeExportCompat.ts: -------------------------------------------------------------------------------- 1 | import set from 'lodash/fp/set' 2 | import { VerboseBoolean } from '../../models/state' 3 | import { Mutator } from '../../types/base' 4 | 5 | /** 6 | * Customizes command insert behavior to either follow or lead the current 7 | * command 8 | */ 9 | export type Shape = (pref: VerboseBoolean) => Promise 10 | 11 | export const mutator: Mutator = (session, { params: [pref] }) => 12 | set('state.userPrefs.disableCodeExportCompat', pref, session) 13 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/state/toggleUserPrefIgnoreCertificateErrors.ts: -------------------------------------------------------------------------------- 1 | import set from 'lodash/fp/set' 2 | import { IgnoreCertificateErrorsPref } from '../../models/state' 3 | import { Mutator } from '../../types/base' 4 | 5 | /** 6 | * Customizes command insert behavior to either follow or lead the current 7 | * command 8 | */ 9 | export type Shape = ( 10 | ignoreCertificateErrorsPref: IgnoreCertificateErrorsPref 11 | ) => Promise 12 | 13 | export const mutator: Mutator = ( 14 | session, 15 | { params: [ignoreCertificateErrorsPref] } 16 | ) => 17 | set( 18 | 'state.userPrefs.ignoreCertificateErrorsPref', 19 | ignoreCertificateErrorsPref, 20 | session 21 | ) 22 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/state/toggleUserPrefInsert.ts: -------------------------------------------------------------------------------- 1 | import set from 'lodash/fp/set' 2 | import { InsertCommandPref } from '../../models/state' 3 | import { Mutator } from '../../types/base' 4 | 5 | /** 6 | * Customizes command insert behavior to either follow or lead the current 7 | * command 8 | */ 9 | export type Shape = (insertCommandPref: InsertCommandPref) => Promise 10 | 11 | export const mutator: Mutator = ( 12 | session, 13 | { params: [insertCommandPref] } 14 | ) => set('state.userPrefs.insertCommandPref', insertCommandPref, session) 15 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/state/toggleUserPrefTheme.ts: -------------------------------------------------------------------------------- 1 | import set from 'lodash/fp/set' 2 | import { ThemePref } from '../../models/state' 3 | import { Mutator } from '../../types/base' 4 | 5 | /** 6 | * Customizes command insert behavior to either follow or lead the current 7 | * command 8 | */ 9 | export type Shape = (themePref: ThemePref) => Promise 10 | 11 | export const mutator: Mutator = (session, { params: [themePref] }) => 12 | set('state.userPrefs.themePref', themePref, session) 13 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/state/undo.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * The OG 3 | */ 4 | export type Shape = () => Promise 5 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/state/updateStepSelection.ts: -------------------------------------------------------------------------------- 1 | import update from 'lodash/fp/update' 2 | import { Mutator } from '../../types/base' 3 | import { getActiveTest } from '../../helpers/getActiveData' 4 | import { updateSelection } from '../../helpers/updateSelection' 5 | 6 | /** 7 | * Changes the selected steps. Meant to be instrumented from the 8 | * active test command list using shift keys and all that. 9 | */ 10 | export type Shape = ( 11 | commandIndex: number, 12 | batch: boolean, 13 | add: boolean, 14 | clear: boolean 15 | ) => Promise 16 | 17 | export const mutator: Mutator = (session, { params }) => { 18 | const activeTest = getActiveTest(session) 19 | return update( 20 | `state.editor.selectedCommandIndexes`, 21 | (indexes: number[]) => 22 | updateSelection( 23 | activeTest.commands.map((_cmd, index) => index), 24 | indexes, 25 | indexes.slice(-1)[0], 26 | params 27 | ), 28 | session 29 | ) 30 | } 31 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/state/updateTestSelection.ts: -------------------------------------------------------------------------------- 1 | import update from 'lodash/fp/update' 2 | import { Mutator } from '../../types/base' 3 | import { getActiveSuite } from '../../helpers/getActiveData' 4 | import { updateSelection } from '../../helpers/updateSelection' 5 | 6 | /** 7 | * Changes the selected tests. Meant to be instrumented from the 8 | * suite editor using shift keys and all that. 9 | */ 10 | export type Shape = ( 11 | testIndex: number, 12 | batch: boolean, 13 | add: boolean, 14 | clear: boolean 15 | ) => Promise 16 | 17 | export const mutator: Mutator = (session, { params }) => 18 | update( 19 | `state.editor.selectedTestIndexes`, 20 | (testIndexes: number[]) => 21 | updateSelection( 22 | getActiveSuite(session).tests.map((_, index) => index), 23 | testIndexes, 24 | testIndexes.slice(-1)[0], 25 | params 26 | ), 27 | session 28 | ) 29 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/suites/addTests.ts: -------------------------------------------------------------------------------- 1 | import update from 'lodash/fp/update' 2 | import { CoreSessionData, Mutator } from '../../types/base' 3 | import { hasID } from '../../helpers/hasID' 4 | 5 | /** 6 | * Adds tests to the chosen suite 7 | */ 8 | export type Shape = ( 9 | suiteID: string, 10 | testIDs: string[], 11 | index: number 12 | ) => Promise 13 | 14 | export const mutator: Mutator = ( 15 | session, 16 | { params: [suiteID, testIDs, index] } 17 | ) => 18 | update( 19 | 'project.suites', 20 | (suites: CoreSessionData['project']['suites']) => { 21 | const suiteIndex = suites.findIndex(hasID(suiteID)) 22 | return update( 23 | `${suiteIndex}.tests`, 24 | (tests) => { 25 | tests.splice(index, 0, ...testIDs) 26 | return tests 27 | }, 28 | suites 29 | ) 30 | }, 31 | session 32 | ) 33 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/suites/create.ts: -------------------------------------------------------------------------------- 1 | import { SuiteShape } from '@seleniumhq/side-model' 2 | import update from 'lodash/fp/update' 3 | import { Mutator } from '../../types/base' 4 | 5 | /** 6 | * Creates a new suite 7 | */ 8 | export type Shape = (name?: string) => Promise 9 | 10 | export const mutator: Mutator = (session, { result }) => 11 | update('project.suites', (suites) => suites.concat(result), session) 12 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/suites/delete.ts: -------------------------------------------------------------------------------- 1 | import update from 'lodash/fp/update' 2 | import { hasID, notHasID } from '../../helpers/hasID' 3 | import { CoreSessionData, Mutator } from '../../types/base' 4 | 5 | /** 6 | * Deletes the selected suite 7 | */ 8 | export type Shape = (suiteID: string) => Promise 9 | 10 | export const mutator: Mutator = (session, { params: [suiteID] }) => { 11 | const sessionWithoutSuite: CoreSessionData = update( 12 | 'project.suites', 13 | (suites) => suites.filter(notHasID(suiteID)), 14 | session 15 | ) 16 | const oldSuites = session.project.suites 17 | const suiteIndex = oldSuites.findIndex(hasID(suiteID)) 18 | const newSuiteIndex = Math.min(suiteIndex, oldSuites.length - 2) 19 | const newActiveSuite = sessionWithoutSuite.project.suites[newSuiteIndex] 20 | return { 21 | ...sessionWithoutSuite, 22 | state: { 23 | ...sessionWithoutSuite.state, 24 | activeSuiteID: newActiveSuite.id, 25 | }, 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/suites/removeTests.ts: -------------------------------------------------------------------------------- 1 | import { SuiteShape } from '@seleniumhq/side-model' 2 | import update from 'lodash/fp/update' 3 | import { Mutator } from '../../types/base' 4 | import { hasID } from '../../helpers/hasID' 5 | 6 | /** 7 | * Removes tests from the chosen suite 8 | */ 9 | export type Shape = (suiteID: string, testIDs: string[]) => Promise 10 | export const mutator: Mutator = ( 11 | session, 12 | { params: [suiteID, testIDs] } 13 | ) => 14 | update( 15 | 'project.suites', 16 | (suites: SuiteShape[]) => { 17 | const suiteIndex = suites.findIndex(hasID(suiteID)) 18 | return update( 19 | `${suiteIndex}.tests`, 20 | (tests: SuiteShape['tests']) => 21 | tests.filter((id) => !testIDs.includes(id)), 22 | suites 23 | ) 24 | }, 25 | session 26 | ) 27 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/suites/update.ts: -------------------------------------------------------------------------------- 1 | import { SuiteShape } from '@seleniumhq/side-model' 2 | import { hasID } from '../../helpers/hasID' 3 | import { CoreSessionData, Mutator } from '../../types/base' 4 | import merge from 'lodash/fp/merge' 5 | import update from 'lodash/fp/update' 6 | 7 | /** 8 | * Changes the metadata of the selected suite 9 | */ 10 | export type Shape = ( 11 | suiteID: string, 12 | updates: Partial> 13 | ) => Promise 14 | 15 | export const mutator: Mutator = ( 16 | session: CoreSessionData, 17 | { params: [suiteID, updates] } 18 | ) => { 19 | const suiteIndex = session.project.suites.findIndex(hasID(suiteID)) 20 | const updatedSession = update( 21 | `project.suites[${suiteIndex}]`, 22 | (suite: SuiteShape) => merge(suite, updates), 23 | session 24 | ) 25 | return updatedSession 26 | } 27 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/system/crash.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Raises an error and exits 3 | */ 4 | export type Shape = (error: unknown) => Promise 5 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/system/getLanguage.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 获取语言设置 3 | */ 4 | export type Shape = () => Promise 5 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/system/getLanguageMap.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 获取语言设置字典 3 | */ 4 | export type Shape = (frontend?: boolean) => Promise 5 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/system/getLogPath.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Shows an open file dialog and passes back the async result 3 | */ 4 | export type Shape = () => Promise 5 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/system/onLog.ts: -------------------------------------------------------------------------------- 1 | import { BaseListener } from '../../types/base' 2 | 3 | /** 4 | * Called when the project editor asks the recorder to highlight an element 5 | * on the playback page. 6 | */ 7 | export type Shape = BaseListener 8 | export type OnLogSystem = [ 9 | 'silly' | 'debug' | 'verbose' | 'info' | 'warn' | 'error', 10 | string 11 | ] 12 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/system/quit.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Exits without raising an error 3 | */ 4 | export type Shape = (error: unknown) => Promise 5 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/system/setLanguage.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 设置语言 3 | */ 4 | export type Shape = (value: string) => Promise 5 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/tests/create.ts: -------------------------------------------------------------------------------- 1 | import { TestShape } from '@seleniumhq/side-model' 2 | import update from 'lodash/fp/update' 3 | import { Mutator } from '../../types/base' 4 | 5 | /** 6 | * Create a new test 7 | */ 8 | export type Shape = (name?: string) => Promise 9 | 10 | export const mutator: Mutator = (session, { result }) => 11 | update('project.tests', (tests) => tests.concat(result), session) 12 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/tests/rename.ts: -------------------------------------------------------------------------------- 1 | import { TestShape } from '@seleniumhq/side-model' 2 | import set from 'lodash/fp/set' 3 | import update from 'lodash/fp/update' 4 | import { hasID } from '../../helpers/hasID' 5 | import { CoreSessionData, Mutator } from '../../types/base' 6 | 7 | /** 8 | * Rename a test 9 | */ 10 | export type Shape = (testID: string, name: string) => Promise 11 | 12 | export const mutator: Mutator = ( 13 | session: CoreSessionData, 14 | { params: [testID, name] } 15 | ) => 16 | update( 17 | 'project.tests', 18 | (tests: TestShape[]) => { 19 | const testIndex = tests.findIndex(hasID(testID)) 20 | return set(`${testIndex}.name`, name, tests) 21 | }, 22 | session 23 | ) 24 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/windows/close.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Close a window by name 3 | */ 4 | export type Shape = (name: string) => Promise 5 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/windows/closePlaybackWindow.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Close a playback window 3 | */ 4 | export type Shape = (id: number) => Promise 5 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/windows/focusPlaybackWindow.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Focus a playback window 3 | */ 4 | export type Shape = (id: number) => Promise 5 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/windows/navigatePlaybackWindow.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Change a playback window url 3 | */ 4 | export type Shape = (id: number, url: string) => Promise 5 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/windows/onPlaybackWindowChanged.ts: -------------------------------------------------------------------------------- 1 | import { BaseListener } from '../../types/base' 2 | 3 | /** 4 | * Runs when a playback window is updated 5 | */ 6 | export type OnPlaybackWindowChanged = [ 7 | id: number, 8 | update: Partial<{ 9 | focused: boolean 10 | state: 'idle' | 'playing' | 'recording' | 'paused' 11 | test: string 12 | title: string 13 | url: string 14 | visible: boolean 15 | }> 16 | ] 17 | export type Shape = BaseListener 18 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/windows/onPlaybackWindowClosed.ts: -------------------------------------------------------------------------------- 1 | import { BaseListener } from '../../types/base' 2 | 3 | /** 4 | * Runs when a playback window is closed 5 | */ 6 | export type OnPlaybackWindowClosed = [id: number] 7 | export type Shape = BaseListener 8 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/windows/onPlaybackWindowOpened.ts: -------------------------------------------------------------------------------- 1 | import { BaseListener } from '../../types/base' 2 | 3 | /** 4 | * Runs when a playback window is opened 5 | */ 6 | export type OnPlaybackWindowOpened = [ 7 | id: number, 8 | update: { 9 | test?: string 10 | title?: string 11 | url: string 12 | } 13 | ] 14 | export type Shape = BaseListener 15 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/windows/open.ts: -------------------------------------------------------------------------------- 1 | import type { BrowserWindowConstructorOptions } from 'electron' 2 | 3 | /** 4 | * Open a window by name with options 5 | */ 6 | export type Shape = ( 7 | name: string, 8 | opts?: BrowserWindowConstructorOptions 9 | ) => Promise 10 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/windows/openCustom.ts: -------------------------------------------------------------------------------- 1 | import type { BrowserWindowConstructorOptions } from 'electron' 2 | 3 | /** 4 | * Open a window by name with options 5 | */ 6 | export type Shape = ( 7 | name: string, 8 | filepath: string, 9 | opts?: BrowserWindowConstructorOptions 10 | ) => Promise 11 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/windows/requestCustomEditorPanel.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Load a custom editor panel from a URL (file or http). 3 | */ 4 | export type Shape = (name: string, url: string) => Promise 5 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/windows/requestPlaybackWindow.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Open a playback window 3 | */ 4 | export type Shape = (url?: string) => Promise 5 | -------------------------------------------------------------------------------- /packages/side-api/src/commands/windows/shiftFocus.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Shift focus b/w the project editor and the playback window 3 | */ 4 | export type Shape = (target: 'editor' | 'playback') => Promise 5 | -------------------------------------------------------------------------------- /packages/side-api/src/constants/badIndex.ts: -------------------------------------------------------------------------------- 1 | export const badIndex = -1 2 | -------------------------------------------------------------------------------- /packages/side-api/src/constants/index.ts: -------------------------------------------------------------------------------- 1 | export * from './badIndex' 2 | export * from './loadingID' 3 | -------------------------------------------------------------------------------- /packages/side-api/src/constants/loadingID.ts: -------------------------------------------------------------------------------- 1 | export const loadingID = '-1' 2 | -------------------------------------------------------------------------------- /packages/side-api/src/helpers/hasID.ts: -------------------------------------------------------------------------------- 1 | export const hasID = (id: string) => (obj: { id: string }) => obj.id === id 2 | 3 | export const notHasID = (id: string) => (obj: { id: string }) => obj.id !== id -------------------------------------------------------------------------------- /packages/side-api/src/helpers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './getActiveData' 2 | export * from './hasID' 3 | export * from './index' 4 | export * from './reorderList' 5 | export * from './updateSelection' 6 | -------------------------------------------------------------------------------- /packages/side-api/src/helpers/string.ts: -------------------------------------------------------------------------------- 1 | export const camelToTitleCase = (text: string) => { 2 | const result = text.replace(/([A-Z])/g, ' $1') 3 | return result.charAt(0).toUpperCase() + result.slice(1) 4 | } 5 | -------------------------------------------------------------------------------- /packages/side-api/src/helpers/updateSelection.ts: -------------------------------------------------------------------------------- 1 | export type UpdateSelection = ( 2 | indexes: number[], 3 | selectionIndexes: number[], 4 | prevIndex: number, 5 | command: [number, boolean, boolean, boolean] 6 | ) => number[] 7 | 8 | export const updateSelection: UpdateSelection = ( 9 | indexes, 10 | selectionIndexes, 11 | prevIndex, 12 | [index, batch, add, clear] 13 | ) => { 14 | let newIndexes = [index] 15 | if (clear) { 16 | return newIndexes 17 | } 18 | if (batch) { 19 | const min = Math.min(prevIndex, index) 20 | const max = Math.max(prevIndex, index) + 1 21 | newIndexes = indexes.slice(min, max) 22 | if (prevIndex > index) { 23 | newIndexes.reverse() 24 | } 25 | } 26 | const filteredIndexes = selectionIndexes.filter((index) => 27 | !newIndexes.includes(index) 28 | ) 29 | if (add) { 30 | return filteredIndexes.concat(newIndexes) 31 | } 32 | return filteredIndexes 33 | } 34 | -------------------------------------------------------------------------------- /packages/side-api/src/models/index.ts: -------------------------------------------------------------------------------- 1 | import { ProjectShape } from '@seleniumhq/side-model' 2 | import { StateShape } from './state' 3 | 4 | export interface AppState { 5 | project: ProjectShape 6 | state: StateShape 7 | } 8 | 9 | export * from './project' 10 | export * from './state' 11 | -------------------------------------------------------------------------------- /packages/side-api/src/models/project/command.ts: -------------------------------------------------------------------------------- 1 | import { CommandShape } from '@seleniumhq/side-model' 2 | import { loadingID } from '../../constants/loadingID' 3 | 4 | /** 5 | * Command shape is the shape of command data in Selenium IDE 6 | * step files 7 | */ 8 | 9 | export const command: CommandShape = { 10 | command: '', 11 | comment: '', 12 | id: loadingID, 13 | target: '', 14 | targets: [], 15 | value: '', 16 | } 17 | -------------------------------------------------------------------------------- /packages/side-api/src/models/project/index.ts: -------------------------------------------------------------------------------- 1 | export * from './command' 2 | export * from './index' 3 | export * from './project' 4 | export * from './snapshot' 5 | export * from './snapshotTest' 6 | export * from './suite' 7 | export * from './test' 8 | -------------------------------------------------------------------------------- /packages/side-api/src/models/project/project.ts: -------------------------------------------------------------------------------- 1 | import { ProjectShape } from '@seleniumhq/side-model' 2 | import { test } from './test' 3 | import { snapshot } from './snapshot' 4 | import { loadingID } from '../../constants/loadingID' 5 | 6 | /** 7 | * This is the actual shape of a side file, 8 | * as well as a key part of our main state tree. 9 | */ 10 | 11 | export const project: ProjectShape = { 12 | id: loadingID, 13 | name: 'loading', 14 | url: 'http://loading', 15 | version: '3.0', 16 | urls: [], 17 | plugins: [], 18 | suites: [], 19 | tests: [test], 20 | snapshot: snapshot, 21 | } 22 | -------------------------------------------------------------------------------- /packages/side-api/src/models/project/snapshot.ts: -------------------------------------------------------------------------------- 1 | import { SnapshotShape } from '@seleniumhq/side-model' 2 | 3 | export const snapshot: SnapshotShape = { 4 | tests: [], 5 | dependencies: {}, 6 | jest: { 7 | extraGlobals: [], 8 | }, 9 | } 10 | -------------------------------------------------------------------------------- /packages/side-api/src/models/project/snapshotTest.ts: -------------------------------------------------------------------------------- 1 | import { SnapshotTestShape } from '@seleniumhq/side-model' 2 | import { loadingID } from '../../constants/loadingID' 3 | 4 | export const snapshotTest: SnapshotTestShape = { 5 | id: loadingID, 6 | snapshot: { 7 | commands: {}, 8 | setupHooks: [], 9 | teardownHooks: [], 10 | }, 11 | } 12 | -------------------------------------------------------------------------------- /packages/side-api/src/models/project/suite.ts: -------------------------------------------------------------------------------- 1 | import { SuiteShape } from '@seleniumhq/side-model' 2 | import { loadingID } from '../../constants/loadingID' 3 | 4 | export const suite: SuiteShape = { 5 | id: loadingID, 6 | name: '', 7 | persistSession: false, 8 | parallel: false, 9 | timeout: 30000, 10 | tests: [], 11 | } 12 | -------------------------------------------------------------------------------- /packages/side-api/src/models/project/test.ts: -------------------------------------------------------------------------------- 1 | import { TestShape } from '@seleniumhq/side-model' 2 | import { command } from './command' 3 | import { loadingID } from '../../constants/loadingID' 4 | 5 | export const test: TestShape = { 6 | id: loadingID, 7 | name: '', 8 | commands: [command], 9 | } 10 | -------------------------------------------------------------------------------- /packages/side-api/src/models/state/command.ts: -------------------------------------------------------------------------------- 1 | import { PlaybackEventShapes } from '@seleniumhq/side-runtime' 2 | 3 | export type CommandStateShape = PlaybackEventShapes['COMMAND_STATE_CHANGED'] 4 | 5 | export type CommandsStateShape = Record 6 | -------------------------------------------------------------------------------- /packages/side-api/src/process.ts: -------------------------------------------------------------------------------- 1 | import commands from './index' 2 | import { GenericApi, ApiEntry, ApiHandler, ApiNamespace } from './types/base' 3 | 4 | export const processApi = ( 5 | handler: ApiHandler 6 | ): FinalApi => { 7 | const api: Partial = {} 8 | for (const ns in commands) { 9 | const namespace: ApiNamespace = commands[ns] 10 | const entry: Partial = {} 11 | for (const ep in namespace) { 12 | const endpoint = namespace[ep] as ApiEntry 13 | entry[ep] = handler(`${ns}.${ep}`, endpoint) 14 | } 15 | // @ts-expect-error 16 | api[ns] = entry as ApiNamespace 17 | } 18 | return api as FinalApi 19 | } 20 | -------------------------------------------------------------------------------- /packages/side-api/src/types/index.ts: -------------------------------------------------------------------------------- 1 | export * from '@seleniumhq/side-model/dist/types' 2 | export * from './api' 3 | export * from './base' 4 | export * from './plugin' 5 | -------------------------------------------------------------------------------- /packages/side-api/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "baseUrl": "./src", 5 | "rootDir": "src", 6 | "outDir": "dist" 7 | }, 8 | "include": ["src/**/*.ts"], 9 | "exclude": ["**/__mocks__"], 10 | "references": [ 11 | { 12 | "path": "../browser-info" 13 | }, 14 | { 15 | "path": "../get-driver" 16 | }, 17 | { 18 | "path": "../side-model" 19 | }, 20 | { 21 | "path": "../side-runtime" 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /packages/side-cli/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-react" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /packages/side-cli/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@npm//@babel/cli:index.bzl", "babel") 2 | 3 | babel( 4 | name = "build", 5 | args = [ 6 | '--root-mode upward -d dist src --extensions ".js,.jsx,.ts,.tsx" --source-maps true' 7 | ] 8 | ) 9 | -------------------------------------------------------------------------------- /packages/side-cli/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "include": ["src/**/*"], 4 | "compilerOptions": { 5 | "jsx": "react", 6 | "outDir": "dist", 7 | "rootDir": "src", 8 | "types": ["node"] 9 | }, 10 | "references": [ 11 | { 12 | "path": "../side-model" 13 | }, 14 | { 15 | "path": "../side-runtime" 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /packages/side-code-export/README.md: -------------------------------------------------------------------------------- 1 | # SIDE Utils 2 | 3 | Utils for Selenium IDE (e.g., code export) 4 | 5 | ## Installation 6 | 7 | ```js 8 | npm install -g @seleniumhq/side-code-export 9 | ``` 10 | 11 | ## Usage 12 | 13 | ```javascript 14 | const { codeExport } = require('@seleniumhq/side-code-export') 15 | ``` 16 | 17 | __NOTE: For an example of usage, see one of the existing languages for code export in Selenium IDE (e.g., [`java-junit`](https://github.com/SeleniumHQ/selenium-ide/tree/trunk/packages/code-export-java-junit), [`javascript-mocha`](https://github.com/SeleniumHQ/selenium-ide/tree/trunk/packages/code-export-javascript-mocha), or [`python-pytest`](https://github.com/SeleniumHQ/selenium-ide/tree/trunk/packages/code-export-python-pytest)), ['csharp-xunit'](https://github.com/SeleniumHQ/selenium-ide/tree/trunk/packages/code-export-csharp-xunit).__ 18 | -------------------------------------------------------------------------------- /packages/side-code-export/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "side-code-export", 3 | "version": "4.0.13", 4 | "description": "Utils for code export from Selenium IDE", 5 | "repository": "https://github.com/SeleniumHQ/selenium-ide", 6 | "keywords": [ 7 | "selenium", 8 | "ide", 9 | "export" 10 | ], 11 | "license": "Apache-2.0", 12 | "scripts": { 13 | "build": "tsc", 14 | "clean": "rm -rf dist tsconfig.tsbuildinfo node_modules", 15 | "watch": "tsc --watch" 16 | }, 17 | "bin": { 18 | "side-code-export": "dist/bin.js" 19 | }, 20 | "main": "dist/index.js", 21 | "types": "dist/index.d.ts", 22 | "dependencies": { 23 | "@seleniumhq/side-model": "4.0.13", 24 | "@seleniumhq/side-runtime": "4.0.13", 25 | "commander": "^9.4.0" 26 | }, 27 | "gitHead": "f58e327e7616e23a3e926e4b80cf9952164e5744" 28 | } 29 | -------------------------------------------------------------------------------- /packages/side-code-export/src/code-export/utils.ts: -------------------------------------------------------------------------------- 1 | import { ExportCommandShape, ExportCommandsShape } from '../types' 2 | 3 | export interface WriteCommandOpts { 4 | commandPrefixPadding: string 5 | level: number 6 | } 7 | 8 | export const writeCommand = ( 9 | command: ExportCommandShape, 10 | { commandPrefixPadding, level }: WriteCommandOpts 11 | ) => 12 | typeof command === 'string' 13 | ? `${commandPrefixPadding.repeat(level) + command}` 14 | : `${commandPrefixPadding.repeat(command.level) + command.statement}` 15 | 16 | export const writeCommands = ( 17 | commandBlock: ExportCommandsShape, 18 | opts: WriteCommandOpts 19 | ) => 20 | commandBlock.commands 21 | .map((cmd) => writeCommand(cmd, opts)) 22 | .join(`\n${opts.commandPrefixPadding}`) 23 | .replace(/^/, opts.commandPrefixPadding) 24 | -------------------------------------------------------------------------------- /packages/side-code-export/src/file-writer/emit-suite.ts: -------------------------------------------------------------------------------- 1 | import { ProjectShape, SuiteShape } from '@seleniumhq/side-model' 2 | import { LanguageEmitter } from '../types' 3 | 4 | export async function emitSuite( 5 | format: LanguageEmitter, 6 | project: ProjectShape, 7 | suiteName: string 8 | ) { 9 | return format.emit.suite({ 10 | baseUrl: project.url, 11 | beforeEachOptions: {}, 12 | enableDescriptionAsComment: true, 13 | enableOriginTracing: false, 14 | project, 15 | suite: project.suites.find((s) => s.name === suiteName) as SuiteShape, 16 | tests: project.tests, 17 | }) 18 | } 19 | -------------------------------------------------------------------------------- /packages/side-code-export/src/file-writer/emit-test.ts: -------------------------------------------------------------------------------- 1 | import { ProjectShape, TestShape } from '@seleniumhq/side-model' 2 | import { LanguageEmitter } from '../types' 3 | 4 | export async function emitTest( 5 | format: LanguageEmitter, 6 | project: ProjectShape, 7 | testName: string 8 | ) { 9 | return format.emit.test({ 10 | baseUrl: project.url, 11 | beforeEachOptions: {}, 12 | enableDescriptionAsComment: true, 13 | enableOriginTracing: false, 14 | project, 15 | test: project.tests.find((t) => t.name === testName) as TestShape, 16 | tests: project.tests, 17 | }) 18 | } 19 | -------------------------------------------------------------------------------- /packages/side-code-export/src/file-writer/index.ts: -------------------------------------------------------------------------------- 1 | import { promises as fs, existsSync as fsExistsSync } from 'fs' 2 | import path from 'path' 3 | 4 | export { emitSuite } from './emit-suite' 5 | export { emitTest } from './emit-test' 6 | 7 | export interface FileWriterOpts { 8 | filename: string 9 | projectPath: string 10 | } 11 | 12 | export async function writeFile( 13 | relativeOrAbsoluteFilepath: string, 14 | file: string, 15 | basePath: string = process.cwd() 16 | ) { 17 | const filepath = path.resolve(basePath, relativeOrAbsoluteFilepath) 18 | const dir = path.dirname(filepath) 19 | if (!fsExistsSync(dir)) { 20 | await fs.mkdir(dir, { recursive: true }) 21 | } 22 | await fs.writeFile(filepath, file) 23 | } 24 | -------------------------------------------------------------------------------- /packages/side-code-export/src/string-escape/index.ts: -------------------------------------------------------------------------------- 1 | export default (input: string, terminatingCharacter = '') => { 2 | if (!input) return '' 3 | return input.replace(/[`'"\\\n\r\u2028\u2029\u005c]/g, (character) => { 4 | switch (character) { 5 | case `"`: 6 | case `'`: 7 | case '`': 8 | if (!terminatingCharacter) return '\\' + character 9 | else if (terminatingCharacter === character) return '\\' + character 10 | else return character 11 | case '\n': 12 | return '\\n' 13 | case '\r': 14 | return '\\r' 15 | case '\u2028': 16 | return '\\u2028' 17 | case '\u2029': 18 | return '\\u2029' 19 | case '\u005c': 20 | return '\\\\' 21 | default: 22 | return character 23 | } 24 | }) 25 | } 26 | -------------------------------------------------------------------------------- /packages/side-code-export/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "baseUrl": "./src", 5 | "rootDir": "src", 6 | "outDir": "dist" 7 | }, 8 | "include": ["src/**/*.ts"], 9 | "exclude": ["**/__mocks__"], 10 | "references": [ 11 | { 12 | "path": "../side-model" 13 | }, 14 | { 15 | "path": "../side-runtime" 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /packages/side-commons/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@npm//@babel/cli:index.bzl", "babel") 2 | 3 | babel( 4 | name = "build", 5 | args = [ 6 | '--root-mode upward -d dist src --extensions ".js,.jsx,.ts,.tsx" --source-maps true' 7 | ] 8 | ) 9 | -------------------------------------------------------------------------------- /packages/side-commons/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@seleniumhq/side-commons", 3 | "version": "4.0.13", 4 | "private": false, 5 | "description": "Selenium IDE common utilities", 6 | "author": "Tomer ", 7 | "homepage": "http://github.com/SeleniumHQ/selenium-ide", 8 | "license": "Apache-2.0", 9 | "scripts": { 10 | "build": "tsc", 11 | "clean": "rm -rf dist tsconfig.tsbuildinfo node_modules", 12 | "watch": "tsc --watch" 13 | }, 14 | "main": "dist/index.js", 15 | "types": "dist/index.d.ts", 16 | "files": [ 17 | "dist" 18 | ], 19 | "repository": { 20 | "type": "git", 21 | "url": "git+https://github.com/SeleniumHQ/selenium-ide.git" 22 | }, 23 | "bugs": { 24 | "url": "https://github.com/SeleniumHQ/selenium-ide/issues" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/side-commons/src/async.ts: -------------------------------------------------------------------------------- 1 | export async function retry( 2 | fn: () => Promise, 3 | retries = 3, 4 | delay = 100 5 | ): Promise { 6 | try { 7 | return await fn() 8 | } catch (e) { 9 | if (retries > 0) { 10 | await new Promise((resolve) => setTimeout(resolve, delay)) 11 | return retry(fn, retries - 1, delay) 12 | } 13 | throw e 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/side-commons/src/events.ts: -------------------------------------------------------------------------------- 1 | import { EventEmitterKey, EventEmitterLike, Fn } from './types' 2 | 3 | export function mergeEventEmitter( 4 | target: any, 5 | emitter: EventEmitterLike 6 | ): void { 7 | const whiteList: EventEmitterKey[] = [ 8 | 'addListener', 9 | 'removeListener', 10 | 'listenerCount', 11 | 'on', 12 | 'off', 13 | 'once', 14 | 'prependListener', 15 | 'prependOnceListener', 16 | ] 17 | whiteList.forEach((emitterProperty) => { 18 | target[emitterProperty] = emitter[emitterProperty] 19 | ? (emitter[emitterProperty] as Fn).bind(emitter) 20 | : undefined 21 | }) 22 | } 23 | -------------------------------------------------------------------------------- /packages/side-commons/src/index.ts: -------------------------------------------------------------------------------- 1 | // Licensed to the Software Freedom Conservancy (SFC) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The SFC licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | export * from './async' 19 | export * as events from './events' 20 | export * as string from './string' 21 | export * from './types' 22 | -------------------------------------------------------------------------------- /packages/side-commons/src/string.ts: -------------------------------------------------------------------------------- 1 | export const capitalize = (str: string) => 2 | str.split(' ').map((word) => `${word[0].toUpperCase()}${word.slice(1)}`) 3 | 4 | export function hyphenToCamelCase(input: string): string { 5 | return input.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase()) 6 | } 7 | -------------------------------------------------------------------------------- /packages/side-commons/src/types.ts: -------------------------------------------------------------------------------- 1 | export type Fn = (...args: any[]) => any 2 | export interface EventEmitterLike { 3 | addListener?: Fn 4 | removeListener?: Fn 5 | listenerCount?: Fn 6 | on?: Fn 7 | off?: Fn 8 | once?: Fn 9 | prependListener?: Fn 10 | prependOnceListener?: Fn 11 | } 12 | export type EventEmitterKey = keyof EventEmitterLike 13 | -------------------------------------------------------------------------------- /packages/side-commons/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "baseUrl": "./src", 5 | "rootDir": "src", 6 | "outDir": "dist" 7 | }, 8 | "include": ["src/**/*.ts"], 9 | "exclude": ["**/__mocks__"] 10 | } 11 | -------------------------------------------------------------------------------- /packages/side-example-suite/README.md: -------------------------------------------------------------------------------- 1 | # SIDE Example Suite 2 | 3 | This contains a project with some nice boilerplate around code export and building 4 | custom plugins with types and such. 5 | 6 | If you don't care about modifying the export format you're starting from, you can 7 | just import it directly in the bin command: 8 | -------------------------------------------------------------------------------- /packages/side-example-suite/src/formats/custom-python.ts: -------------------------------------------------------------------------------- 1 | import language from '@seleniumhq/code-export-python-pytest' 2 | 3 | /** 4 | * This is an example of taking a stock export format and modifying it 5 | * to add a custom command. 6 | * 7 | * To modify it further, I would recommend looking at the source code 8 | * for the format you are modifying. 9 | */ 10 | 11 | language.register.command( 12 | 'customClick', 13 | async (target) => 14 | `self.driver.find_element(${await language.emit.locator(target)}).click()` 15 | ) 16 | 17 | export default language 18 | -------------------------------------------------------------------------------- /packages/side-example-suite/src/plugins/custom-click/preload/index.ts: -------------------------------------------------------------------------------- 1 | import { Api } from '@seleniumhq/side-api' 2 | 3 | // @ts-expect-error - this is an exposed global 4 | const api = window.sideAPI as Api 5 | api.plugins.addRecorderPreprocessor((command, event) => { 6 | console.debug('Recorded command', command, event) 7 | if (command.command !== 'click') return 8 | api.channels.send('example-plugin', command) 9 | return { 10 | action: 'update', 11 | command: { 12 | ...command, 13 | command: 'customClick', 14 | }, 15 | } 16 | }) 17 | 18 | export = true 19 | -------------------------------------------------------------------------------- /packages/side-example-suite/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "baseUrl": "./src", 5 | "rootDir": "src", 6 | "outDir": "dist" 7 | }, 8 | "include": ["src/**/*.ts"], 9 | "exclude": ["**/__mocks__"], 10 | "references": [ 11 | { 12 | "path": "../code-export-python-pytest" 13 | }, 14 | { 15 | "path": "../side-code-export" 16 | }, 17 | { 18 | "path": "../side-api" 19 | }, 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /packages/side-migrate/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@npm//@babel/cli:index.bzl", "babel") 2 | load("@npm//jest-cli:index.bzl", "jest_test") 3 | 4 | babel( 5 | name = "build", 6 | args = [ 7 | '--root-mode upward -d dist src --extensions ".js,.jsx,.ts,.tsx" --source-maps true' 8 | ] 9 | ) 10 | 11 | filegroup( 12 | name = "test_lib", 13 | srcs = glob([ 14 | "**/*.js" 15 | ]), 16 | ) 17 | 18 | jest_test( 19 | name = "test", 20 | args = [ 21 | "--no-cache", 22 | "--no-watchman", 23 | "--ci", 24 | "--colors", 25 | "--config", 26 | "babel.config.js", 27 | "--updateSnapshot" 28 | ], 29 | data = [ 30 | ":test_lib", 31 | "//:babel.config.js" 32 | ] 33 | ) 34 | -------------------------------------------------------------------------------- /packages/side-migrate/README.md: -------------------------------------------------------------------------------- 1 | # About 2 | 3 | This package is meant to upgrade commands in a project from v3 to v4 effortlessly. 4 | 5 | ## How to use 6 | 7 | Honestly, just do something like this: 8 | 9 | `npx @seleniumhq/side-migrate ./existing_file.side ./new_v4_file.side` 10 | 11 | After that, your project json should be upgraded to v4. If that doesn't work 12 | right for ya, please raise an issue [here]( 13 | https://github.com/SeleniumHQ/selenium-ide/issues/new?assignees=&labels=&projects=&template=bug.md 14 | ) 15 | 16 | Thanks! 17 | 18 | ## Contributing 19 | 20 | Oh gee golly! Oh gee willikers batman! What a generous offer! <3 I'd start in contributing [here]( 21 | https://github.com/SeleniumHQ/selenium-ide/issues/new?assignees=&labels=&projects=&template=bug.md 22 | ) and you can just go where the flow takes you! 23 | -------------------------------------------------------------------------------- /packages/side-migrate/__tests__/legacy/IDE_test_10.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Log in as test user 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
A command
open/
20 | 21 | 22 | -------------------------------------------------------------------------------- /packages/side-migrate/__tests__/legacy/IDE_test_10/Log out from BO.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Log out from BO 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 |
Log out from BO
open/user/logout
openAndWait/
26 | 27 | 28 | -------------------------------------------------------------------------------- /packages/side-migrate/__tests__/legacy/IDE_test_10/Suite login_multiple cases.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Test Suite 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
Test Suite
Log in as test user
Log out from BO
Log in as test user
Log in as test user
16 | 17 | 18 | -------------------------------------------------------------------------------- /packages/side-migrate/__tests__/legacy/IDE_test_4/000_clear_mandant_Suite.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Test Suite 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
Test Suite
MH_delete
kontakte_leeren
DMS_clear
15 | 16 | 17 | -------------------------------------------------------------------------------- /packages/side-migrate/__tests__/legacy/IDE_test_4/001_clear_mandant_Suite.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Test Suite 2 7 | 8 | 9 | 10 | 11 | 12 | 13 |
Test Suite 2
MH_delete
DMS_clear
14 | 15 | 16 | -------------------------------------------------------------------------------- /packages/side-migrate/__tests__/legacy/IDE_test_wait.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Log in as test user 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
A command
waitForPageToLoad
waitForElementPresent
25 | 26 | 27 | -------------------------------------------------------------------------------- /packages/side-migrate/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testEnvironmentOptions: { 3 | url: 'http://localhost/index.html', 4 | }, 5 | testMatch: ['**/packages/**/__test?(s)__/**/*.spec.[jt]s?(x)'], 6 | testPathIgnorePatterns: ['/node_modules/', '/dist/'], 7 | transform: { 8 | '^.+\\.ts?$': 'ts-jest', 9 | }, 10 | } 11 | -------------------------------------------------------------------------------- /packages/side-migrate/src/bin.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import fs from 'fs' 3 | import UpgradeProject from './migrate' 4 | 5 | const inputFilepath = process.argv[2] 6 | const outputFilepath = process.argv[3] 7 | console.log(process.argv) 8 | if (!inputFilepath) { 9 | throw new Error('No input file path provided') 10 | } 11 | if (!outputFilepath) { 12 | throw new Error('No output file path provided') 13 | } 14 | if (!inputFilepath.endsWith('.side')) { 15 | throw new Error('Input file path must be a .side file') 16 | } 17 | 18 | const project = JSON.parse(fs.readFileSync(inputFilepath, 'utf8')) 19 | const newProject = UpgradeProject(project) 20 | 21 | fs.writeFileSync(outputFilepath, JSON.stringify(newProject, null, 2)) 22 | 23 | console.log( 24 | `Upgraded ${inputFilepath} to version ${newProject.version} and saved to ${outputFilepath}` 25 | ) 26 | -------------------------------------------------------------------------------- /packages/side-migrate/src/types.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'unescape' { 2 | type Unescape = (str: string) => string 3 | const unescape: Unescape 4 | export = unescape 5 | } 6 | -------------------------------------------------------------------------------- /packages/side-migrate/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "baseUrl": "./src", 5 | "rootDir": "src", 6 | "outDir": "dist" 7 | }, 8 | "include": ["src/**/*.ts"], 9 | "exclude": ["**/__mocks__"], 10 | "references": [ 11 | { 12 | "path": "../side-model" 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /packages/side-model/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@npm//@bazel/typescript:index.bzl", "ts_library") 2 | load("@npm//jest-cli:index.bzl", "jest_test") 3 | 4 | exports_files(["tsconfig.json"], visibility=["//visibility:public"]) 5 | 6 | ts_library( 7 | name="build", 8 | srcs=glob(["src/**/*.ts"]), 9 | deps=[ 10 | "@npm//@types/node" 11 | ] 12 | ) 13 | 14 | ts_library( 15 | name = "test_lib", 16 | srcs = glob([ 17 | "src/**/*.ts", 18 | "__tests__/**/*.ts", 19 | ]), 20 | deps = [ 21 | "@npm//@types" 22 | ], 23 | ) 24 | 25 | jest_test( 26 | name = "test", 27 | args = [ 28 | "--no-cache", 29 | "--no-watchman", 30 | "--ci", 31 | "--colors", 32 | "--config", 33 | "jest.config.js" 34 | ], 35 | data = [ 36 | ":test_lib", 37 | "//:jest.config.js" 38 | ] 39 | ) 40 | -------------------------------------------------------------------------------- /packages/side-model/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@seleniumhq/side-model", 3 | "version": "4.0.13", 4 | "private": false, 5 | "description": "Selenium IDE shared models", 6 | "author": "Tomer ", 7 | "homepage": "http://github.com/SeleniumHQ/selenium-ide", 8 | "license": "Apache-2.0", 9 | "scripts": { 10 | "build": "tsc", 11 | "clean": "rm -rf dist tsconfig.tsbuildinfo node_modules", 12 | "watch": "tsc --watch" 13 | }, 14 | "main": "dist/index.js", 15 | "types": "dist/index.d.ts", 16 | "files": [ 17 | "dist" 18 | ], 19 | "repository": { 20 | "type": "git", 21 | "url": "git+https://github.com/SeleniumHQ/selenium-ide.git" 22 | }, 23 | "bugs": { 24 | "url": "https://github.com/SeleniumHQ/selenium-ide/issues" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/side-model/src/I18N/en/ArgTypes.ts: -------------------------------------------------------------------------------- 1 | import ArgTypes from '../../ArgTypes' 2 | 3 | export default ArgTypes 4 | -------------------------------------------------------------------------------- /packages/side-model/src/I18N/en/Commands.ts: -------------------------------------------------------------------------------- 1 | import Commands from '../../Commands' 2 | 3 | export default Commands 4 | -------------------------------------------------------------------------------- /packages/side-model/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "baseUrl": "./src", 5 | "rootDir": "src", 6 | "outDir": "dist" 7 | }, 8 | "include": ["src/**/*.ts"], 9 | "exclude": ["**/__mocks__"] 10 | } 11 | -------------------------------------------------------------------------------- /packages/side-runner/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [3.7.4](https://github.com/SeleniumHQ/selenium-ide/compare/v3.7.3...v3.7.4) (2019-05-16) 7 | 8 | **Note:** Version bump only for package selenium-side-runner 9 | -------------------------------------------------------------------------------- /packages/side-runner/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testEnvironmentOptions: { 3 | url: 'http://localhost/index.html', 4 | }, 5 | testMatch: ['**/packages/**/__test?(s)__/**/*.spec.[jt]s?(x)'], 6 | testPathIgnorePatterns: ['/node_modules/', '/dist/'], 7 | transform: { 8 | '^.+\\.ts?$': 'ts-jest', 9 | }, 10 | } 11 | -------------------------------------------------------------------------------- /packages/side-runner/runner.jest.config.js: -------------------------------------------------------------------------------- 1 | /** eslint-disable **/ 2 | const path = require('path') 3 | 4 | module.exports = { 5 | reporters: ['default'], 6 | rootDir: path.resolve(__dirname), 7 | testEnvironment: 'node', 8 | testPathIgnorePatterns: ['/node_modules/'], 9 | verbose: true, 10 | } 11 | -------------------------------------------------------------------------------- /packages/side-runner/src/__tests__/config_1.yml: -------------------------------------------------------------------------------- 1 | capabilities: 2 | browserName: chrome 3 | platform: MAC 4 | unexpectedAlertBehaviour: ignore 5 | -------------------------------------------------------------------------------- /packages/side-runner/src/__tests__/config_2.yml: -------------------------------------------------------------------------------- 1 | capabilities: 2 | browserName: chrome 3 | platform: UNIX 4 | -------------------------------------------------------------------------------- /packages/side-runner/src/connect.ts: -------------------------------------------------------------------------------- 1 | import { Variables, WebDriverExecutor } from '@seleniumhq/side-runtime' 2 | import { Configuration } from './types' 3 | 4 | export default async (configuration: Configuration) => { 5 | const driver = new WebDriverExecutor({ 6 | capabilities: JSON.parse(JSON.stringify(configuration.capabilities)), 7 | server: configuration.server, 8 | }) 9 | console.log('Beginning test session') 10 | await driver.init({ 11 | baseUrl: configuration.baseUrl, 12 | debug: true, 13 | logger: console, 14 | variables: new Variables(), 15 | }) 16 | console.log('Test session created') 17 | const session = await driver.driver.getSession() 18 | console.log('Session ID:', session.getId()) 19 | await driver.cleanup() 20 | } 21 | -------------------------------------------------------------------------------- /packages/side-runner/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "baseUrl": "./src", 5 | "rootDir": "src", 6 | "outDir": "dist" 7 | }, 8 | 9 | "include": ["src/**/*.ts"], 10 | "exclude": ["**/__mocks__"], 11 | "references": [ 12 | { 13 | "path": "../side-model" 14 | }, 15 | { 16 | "path": "../side-runtime" 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /packages/side-runtime/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@npm//@babel/cli:index.bzl", "babel") 2 | load("@npm//jest-cli:index.bzl", "jest_test") 3 | 4 | babel( 5 | name = "build", 6 | args = [ 7 | '--root-mode upward -d dist src --extensions ".js,.jsx,.ts,.tsx" --source-maps true' 8 | ] 9 | ) 10 | 11 | filegroup( 12 | name = "test_lib", 13 | srcs = glob([ 14 | "**/*.js" 15 | ]), 16 | ) 17 | 18 | jest_test( 19 | name = "test", 20 | args = [ 21 | "--no-cache", 22 | "--no-watchman", 23 | "--ci", 24 | "--colors", 25 | "--config", 26 | "babel.config.js", 27 | "--updateSnapshot" 28 | ], 29 | data = [ 30 | ":test_lib", 31 | "//:babel.config.js" 32 | ] 33 | ) 34 | -------------------------------------------------------------------------------- /packages/side-runtime/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testEnvironmentOptions: { 3 | url: 'http://localhost/index.html', 4 | }, 5 | testMatch: ['**/packages/**/__test?(s)__/**/*.spec.[jt]s?(x)'], 6 | testPathIgnorePatterns: ['/node_modules/', '/dist/'], 7 | transform: { 8 | '^.+\\.ts?$': 'ts-jest', 9 | }, 10 | } 11 | -------------------------------------------------------------------------------- /packages/side-runtime/src/errors/index.ts: -------------------------------------------------------------------------------- 1 | // Licensed to the Software Freedom Conservancy (SFC) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The SFC licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | export { default as AssertionError } from './assertion' 19 | export { default as VerificationError } from './verification' 20 | -------------------------------------------------------------------------------- /packages/side-runtime/src/utils.ts: -------------------------------------------------------------------------------- 1 | // Licensed to the Software Freedom Conservancy (SFC) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The SFC licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | export function absolutifyUrl(targetUrl: string, baseUrl: string): string { 19 | return new URL(targetUrl, baseUrl).href 20 | } 21 | -------------------------------------------------------------------------------- /packages/side-runtime/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "baseUrl": "./src", 5 | "rootDir": "src", 6 | "outDir": "dist" 7 | }, 8 | "include": ["src/**/*.ts"], 9 | "exclude": ["**/__mocks__"], 10 | "references": [ 11 | { 12 | "path": "../side-commons" 13 | }, 14 | { 15 | "path": "../side-model" 16 | }, 17 | { 18 | "path": "../side-testkit" 19 | }, 20 | { 21 | "path": "../webdriver-testkit" 22 | } 23 | ], 24 | "types": ["node", "jest"] 25 | } 26 | -------------------------------------------------------------------------------- /packages/side-test-metaparser/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testEnvironmentOptions: { 3 | url: 'http://localhost/index.html', 4 | }, 5 | testMatch: ['**/packages/**/__test?(s)__/**/*.spec.[jt]s?(x)'], 6 | testPathIgnorePatterns: ['/node_modules/', '/dist/'], 7 | transform: { 8 | '^.+\\.ts?$': 'ts-jest', 9 | }, 10 | } 11 | -------------------------------------------------------------------------------- /packages/side-test-metaparser/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@seleniumhq/side-test-metaparser", 3 | "version": "4.0.13", 4 | "private": false, 5 | "description": "Reads code to JSON and back", 6 | "author": "Todd Tarsi ", 7 | "homepage": "http://github.com/SeleniumHQ/selenium-ide", 8 | "license": "Apache-2.0", 9 | "scripts": { 10 | "build": "tsc", 11 | "clean": "rm -rf dist tsconfig.tsbuildinfo node_modules", 12 | "try": "node dist/comment.ts", 13 | "watch": "tsc --watch" 14 | }, 15 | "main": "dist/index.js", 16 | "types": "dist/index.d.ts", 17 | "files": [ 18 | "dist" 19 | ], 20 | "repository": { 21 | "type": "git", 22 | "url": "git+https://github.com/SeleniumHQ/selenium-ide.git" 23 | }, 24 | "bugs": { 25 | "url": "https://github.com/SeleniumHQ/selenium-ide/issues" 26 | }, 27 | "gitHead": "507c7c802f34196e6ee4800bf5c0b36553d41369" 28 | } 29 | -------------------------------------------------------------------------------- /packages/side-test-metaparser/src/parsers/click.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeleniumHQ/selenium-ide/5d595cd4c58c504a4b0bc47d3d39c45c636c9c32/packages/side-test-metaparser/src/parsers/click.ts -------------------------------------------------------------------------------- /packages/side-test-metaparser/src/parsers/comment.ts: -------------------------------------------------------------------------------- 1 | export default (commentLine: string) => { 2 | const match = commentLine.match( 3 | // Regex pattern to capture prefix and postfix 4 | /(?^[^a-zA-Z0-9]*)(?COMMENT_STRING)(?[^a-zA-Z0-9]*$)/ 5 | ) 6 | if (!match?.groups?.prefix) { 7 | throw new Error('Unable to parse comment training line: ' + commentLine) 8 | } 9 | return (line: string) => { 10 | const cleanedLine = line.trim() 11 | if (!cleanedLine.startsWith(match!.groups!.prefix)) { 12 | return null 13 | } 14 | const postfix = match?.groups?.postfix 15 | if (postfix && !cleanedLine.endsWith(postfix)) { 16 | return null 17 | } 18 | return cleanedLine.slice( 19 | match!.groups!.prefix.length, 20 | postfix ? -postfix.length : cleanedLine.length 21 | ) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packages/side-test-metaparser/src/parsers/execute-script.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeleniumHQ/selenium-ide/5d595cd4c58c504a4b0bc47d3d39c45c636c9c32/packages/side-test-metaparser/src/parsers/execute-script.ts -------------------------------------------------------------------------------- /packages/side-test-metaparser/src/parsers/find-element-locator.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeleniumHQ/selenium-ide/5d595cd4c58c504a4b0bc47d3d39c45c636c9c32/packages/side-test-metaparser/src/parsers/find-element-locator.ts -------------------------------------------------------------------------------- /packages/side-test-metaparser/src/parsers/log-string-with-variables.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeleniumHQ/selenium-ide/5d595cd4c58c504a4b0bc47d3d39c45c636c9c32/packages/side-test-metaparser/src/parsers/log-string-with-variables.ts -------------------------------------------------------------------------------- /packages/side-test-metaparser/src/parsers/select-element.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeleniumHQ/selenium-ide/5d595cd4c58c504a4b0bc47d3d39c45c636c9c32/packages/side-test-metaparser/src/parsers/select-element.ts -------------------------------------------------------------------------------- /packages/side-test-metaparser/src/parsers/send-keys.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeleniumHQ/selenium-ide/5d595cd4c58c504a4b0bc47d3d39c45c636c9c32/packages/side-test-metaparser/src/parsers/send-keys.ts -------------------------------------------------------------------------------- /packages/side-test-metaparser/src/parsers/set-nested-variable.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeleniumHQ/selenium-ide/5d595cd4c58c504a4b0bc47d3d39c45c636c9c32/packages/side-test-metaparser/src/parsers/set-nested-variable.ts -------------------------------------------------------------------------------- /packages/side-test-metaparser/src/parsers/set-variable.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeleniumHQ/selenium-ide/5d595cd4c58c504a4b0bc47d3d39c45c636c9c32/packages/side-test-metaparser/src/parsers/set-variable.ts -------------------------------------------------------------------------------- /packages/side-test-metaparser/src/parsers/wait-for-attribute.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeleniumHQ/selenium-ide/5d595cd4c58c504a4b0bc47d3d39c45c636c9c32/packages/side-test-metaparser/src/parsers/wait-for-attribute.ts -------------------------------------------------------------------------------- /packages/side-test-metaparser/src/parsers/wait-for-text.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeleniumHQ/selenium-ide/5d595cd4c58c504a4b0bc47d3d39c45c636c9c32/packages/side-test-metaparser/src/parsers/wait-for-text.ts -------------------------------------------------------------------------------- /packages/side-test-metaparser/src/parsers/wait-for-value.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeleniumHQ/selenium-ide/5d595cd4c58c504a4b0bc47d3d39c45c636c9c32/packages/side-test-metaparser/src/parsers/wait-for-value.ts -------------------------------------------------------------------------------- /packages/side-test-metaparser/src/types.ts: -------------------------------------------------------------------------------- 1 | export type Parser> = { 2 | parse: (line: string) => string | null 3 | write: (options: OPTS) => string 4 | } 5 | -------------------------------------------------------------------------------- /packages/side-test-metaparser/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "baseUrl": "./src", 5 | "rootDir": "src", 6 | "outDir": "dist" 7 | }, 8 | "include": ["src/**/*.ts"], 9 | "exclude": ["**/__mocks__"], 10 | "references": [ 11 | { 12 | "path": "../side-commons" 13 | }, 14 | { 15 | "path": "../side-model" 16 | }, 17 | { 18 | "path": "../side-testkit" 19 | }, 20 | { 21 | "path": "../webdriver-testkit" 22 | } 23 | ], 24 | "types": ["node", "jest"] 25 | } 26 | -------------------------------------------------------------------------------- /packages/side-testkit/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@npm//@babel/cli:index.bzl", "babel") 2 | 3 | babel( 4 | name = "build", 5 | args = [ 6 | '--root-mode upward -d dist src --extensions ".js,.jsx,.ts,.tsx" --source-maps true' 7 | ] 8 | ) 9 | -------------------------------------------------------------------------------- /packages/side-testkit/fixtures/static/check.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /packages/side-testkit/fixtures/static/click.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 31 | -------------------------------------------------------------------------------- /packages/side-testkit/fixtures/static/contenteditable.html: -------------------------------------------------------------------------------- 1 |
2 | -------------------------------------------------------------------------------- /packages/side-testkit/fixtures/static/editable.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/side-testkit/fixtures/static/file.html: -------------------------------------------------------------------------------- 1 | 13 | 14 |
15 | 16 | -------------------------------------------------------------------------------- /packages/side-testkit/fixtures/static/form.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 |
5 | -------------------------------------------------------------------------------- /packages/side-testkit/fixtures/static/form2.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 |
5 | -------------------------------------------------------------------------------- /packages/side-testkit/fixtures/static/frames-manual.html: -------------------------------------------------------------------------------- 1 | 13 | 14 |

Manual Frames

15 | 16 | 17 |
18 |
19 | 2 | -------------------------------------------------------------------------------- /packages/side-testkit/fixtures/static/mouse/down.html: -------------------------------------------------------------------------------- 1 | 8 |
-------------------------------------------------------------------------------- /packages/side-testkit/fixtures/static/mouse/index.html: -------------------------------------------------------------------------------- 1 | 23 |
out
24 | -------------------------------------------------------------------------------- /packages/side-testkit/fixtures/static/mouse/move.html: -------------------------------------------------------------------------------- 1 | 19 |
20 | -------------------------------------------------------------------------------- /packages/side-testkit/fixtures/static/mouse/over.html: -------------------------------------------------------------------------------- 1 | 19 |
20 | -------------------------------------------------------------------------------- /packages/side-testkit/fixtures/static/mouse/overout.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 28 | -------------------------------------------------------------------------------- /packages/side-testkit/fixtures/static/mouse/updown.html: -------------------------------------------------------------------------------- 1 | 24 |
25 | -------------------------------------------------------------------------------- /packages/side-testkit/fixtures/static/nested_frame.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/side-testkit/fixtures/static/popup/alert.html: -------------------------------------------------------------------------------- 1 | 7 | 8 | -------------------------------------------------------------------------------- /packages/side-testkit/fixtures/static/popup/confirm.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | -------------------------------------------------------------------------------- /packages/side-testkit/fixtures/static/popup/prompt.html: -------------------------------------------------------------------------------- 1 | 13 | 14 | -------------------------------------------------------------------------------- /packages/side-testkit/fixtures/static/scroll/infinite.html: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | 15 | 27 | 28 | -------------------------------------------------------------------------------- /packages/side-testkit/fixtures/static/select.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | 13 | -------------------------------------------------------------------------------- /packages/side-testkit/fixtures/static/selectors/children.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 6 | 7 |
8 | -------------------------------------------------------------------------------- /packages/side-testkit/fixtures/static/store/attributes.html: -------------------------------------------------------------------------------- 1 | 2 |
content
3 | 4 | -------------------------------------------------------------------------------- /packages/side-testkit/fixtures/static/store/nodes.html: -------------------------------------------------------------------------------- 1 | 2 |
content
3 | span content 4 |
content
5 | span content 6 |
content
7 | span content 8 |
content
9 | span content 10 |
content
11 | span content 12 |
content
13 | span content 14 | span content 15 | span content 16 | span content 17 | span content 18 | -------------------------------------------------------------------------------- /packages/side-testkit/fixtures/static/success.html: -------------------------------------------------------------------------------- 1 |

Success!

2 | -------------------------------------------------------------------------------- /packages/side-testkit/fixtures/static/text.html: -------------------------------------------------------------------------------- 1 |

some test text

2 | -------------------------------------------------------------------------------- /packages/side-testkit/fixtures/static/textarea.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /packages/side-testkit/fixtures/static/title.html: -------------------------------------------------------------------------------- 1 | test title 2 | -------------------------------------------------------------------------------- /packages/side-testkit/fixtures/static/value.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /packages/side-testkit/fixtures/static/wait/editable.html: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /packages/side-testkit/fixtures/static/wait/implicit.html: -------------------------------------------------------------------------------- 1 | 11 | 12 | -------------------------------------------------------------------------------- /packages/side-testkit/fixtures/static/wait/not-editable.html: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /packages/side-testkit/fixtures/static/wait/not-present.html: -------------------------------------------------------------------------------- 1 | 9 | 10 |
hey
11 | -------------------------------------------------------------------------------- /packages/side-testkit/fixtures/static/wait/not-visible.html: -------------------------------------------------------------------------------- 1 | 9 | 10 |
hey
11 | -------------------------------------------------------------------------------- /packages/side-testkit/fixtures/static/wait/present.html: -------------------------------------------------------------------------------- 1 | 11 | 12 | -------------------------------------------------------------------------------- /packages/side-testkit/fixtures/static/wait/visible.html: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /packages/side-testkit/fixtures/static/windows.html: -------------------------------------------------------------------------------- 1 |

This is a window

2 | open new window 3 | -------------------------------------------------------------------------------- /packages/side-testkit/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@seleniumhq/side-testkit", 3 | "version": "4.0.13", 4 | "private": false, 5 | "description": "Selenium IDE test stuff", 6 | "author": "Tomer ", 7 | "homepage": "http://github.com/SeleniumHQ/selenium-ide", 8 | "license": "Apache-2.0", 9 | "scripts": { 10 | "build": "tsc", 11 | "clean": "rm -rf dist tsconfig.tsbuildinfo node_modules", 12 | "watch": "tsc --watch" 13 | }, 14 | "main": "dist/index.js", 15 | "files": [ 16 | "dist" 17 | ], 18 | "repository": { 19 | "type": "git", 20 | "url": "git+https://github.com/SeleniumHQ/selenium-ide.git" 21 | }, 22 | "bugs": { 23 | "url": "https://github.com/SeleniumHQ/selenium-ide/issues" 24 | }, 25 | "dependencies": { 26 | "express": "^4.19.2" 27 | }, 28 | "devDependencies": { 29 | "@types/express": "^4.17.13" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/side-testkit/src/index.ts: -------------------------------------------------------------------------------- 1 | // Licensed to the Software Freedom Conservancy (SFC) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The SFC licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | import _static from './static' 19 | export type { Express } from 'express' 20 | 21 | export const createStaticSite = _static 22 | -------------------------------------------------------------------------------- /packages/side-testkit/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "rootDir": "src", 5 | "outDir": "dist" 6 | }, 7 | "include": ["src/**/*.ts"], 8 | "exclude": ["**/__mocks__"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/webdriver-testkit/scripts/download-drivers.js: -------------------------------------------------------------------------------- 1 | // Licensed to the Software Freedom Conservancy (SFC) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The SFC licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | const { updateDrivers } = require('../') 19 | 20 | updateDrivers() 21 | -------------------------------------------------------------------------------- /packages/webdriver-testkit/src/cache.ts: -------------------------------------------------------------------------------- 1 | // Licensed to the Software Freedom Conservancy (SFC) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The SFC licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | import path from 'path' 19 | 20 | export const CACHE_PATH = path.join(__dirname, '../../../drivers') 21 | -------------------------------------------------------------------------------- /packages/webdriver-testkit/src/index.ts: -------------------------------------------------------------------------------- 1 | // Licensed to the Software Freedom Conservancy (SFC) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The SFC licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | export { updateDrivers } from './update-drivers' 19 | export * from './driver' 20 | -------------------------------------------------------------------------------- /packages/webdriver-testkit/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "rootDir": "src", 5 | "outDir": "dist" 6 | }, 7 | "include": ["src/**/*.ts"], 8 | "exclude": ["**/__mocks__"], 9 | "references": [ 10 | { 11 | "path": "../browser-info" 12 | }, 13 | { 14 | "path": "../get-driver" 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | # all packages in direct subdirs of packages/ 3 | - 'packages/*' -------------------------------------------------------------------------------- /scripts/jest/test.config.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line node/no-unpublished-require 2 | require('@testing-library/jest-dom/extend-expect') // matchers for view tests 3 | 4 | window.HTMLElement.prototype.scrollTo = jest.fn() 5 | 6 | class MutationObserver { 7 | constructor() { 8 | this.observe = jest.fn() 9 | this.disconnect = jest.fn() 10 | } 11 | } 12 | 13 | window.MutationObserver = MutationObserver 14 | -------------------------------------------------------------------------------- /test.config.js: -------------------------------------------------------------------------------- 1 | require('@testing-library/jest-dom/extend-expect') // matchers for view tests 2 | 3 | window.HTMLElement.prototype.scrollTo = jest.fn() 4 | 5 | class MutationObserver { 6 | constructor() { 7 | this.observe = jest.fn() 8 | this.disconnect = jest.fn() 9 | } 10 | } 11 | 12 | window.MutationObserver = MutationObserver 13 | -------------------------------------------------------------------------------- /tsconfig.base.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "lib": ["es5", "es6", "es2017", "es2019", "dom", "dom.iterable"], 5 | "declaration": true, 6 | "declarationMap": true, 7 | "strict": true, 8 | /* Dont fail over type things in node_modules */ 9 | "skipLibCheck": true, 10 | 11 | /* Additional Checks */ 12 | "noUnusedLocals": true, 13 | "noUnusedParameters": true, 14 | "noImplicitReturns": true, 15 | "noFallthroughCasesInSwitch": true, 16 | 17 | /* Module Resolution Options */ 18 | "esModuleInterop": true, 19 | "module": "commonjs", 20 | "moduleResolution": "node", 21 | "sourceMap": true, 22 | "target": "ES2021", 23 | "types": ["node", "jest"] 24 | } 25 | } 26 | --------------------------------------------------------------------------------