uistrategy

package module
v0.2.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 12, 2023 License: MIT Imports: 14 Imported by: 0

README

Bugs Technical Debt Reliability Rating Vulnerabilities Coverage

UI Strategy - Beta

Go Report Card

Config driven UI driver for front end testing, target agnostic, stored in declarative configuration files.

Can be user for data seeding in cases where there isn't an REST API available.

Disclaimer: Part of strategy series :D - see reststrategy 😉 - there is a module and a published CLI which functions in a similar way for REST calls. Always prefer to use that for any kind of configuration/data seeding where possible!

Features

The program splits the instructions into a top level slice which includes a navigation to a part of the app where it will include all the actions against the elements present.

It will go through all of them in sequence as they are defined in the YAML.

Currently all the actions are performed against a single instance of the logged in page. for larger systems where order isn't important separate instances of the CLI can be triggered.

  • Authentication
    • optional authentication
  • Driving UI
    • page Navigation
    • element lookups using a selector either a CSS Style selector or XPath
      • CSSSelector will be tried first and then XPath
    • actions on element currently input + click/swipe - would be nice to include double click/right click, etc...
  • Report
    • report with all steps
    • screenshots on errors attached to the step.
    • report.json - can be used in an HTML template creation, additionally JUnit or any other kind of format can be parsed from that base.
  • ConfigManager integrated for easy storage of secrets in YMLs that can be committed - see this example of a password for auth.

As this is still in beta expect bugs and the interface to change.

Improvement/Feature:

  • accept multiple yaml docs and run them in parallel.
  • allow composing of complete strategies from multiple YAML documents - AVOID 1k+ YAML lines

Configuration

setup

Top level config item to initiate a webBrowser session

baseUrl

must be provided acts as a baseUrl for all navigations, including login.

continueOnError

Default: false

Will stop execution if an error occurs, useful to set this to true if a large execution sequence

auth

...

actions

Is a list of actions to execute - the order in which they are provided.

Single action block has the below structure, at this level the action is only a navigation action/view action. it can contain 0 or more elementActions

name (required)

Name of the view action - will be used in reports

navigate (required)

The path to append to the baseUrl to navigate to perform actions against elements on that page.

navigate string is appended to the baseUrl without any slashes - ensure you either specify the baseUrl with a trailing slash or all your navigates should include a preceeding slash.

iframe (optional)

object with following properties. when the actions you want to perform on that page/view are within an iframe it must be specifed here

selector

the selector for the iframe - using either CSS or Xpath e.g.: (//*/iframe)[1] - i.e. give me the first iframe on the page

waitEval

oftentimes older apps (e.g. timesheet portals 😉 ) include iframes and they are often loaded slower to avoid losing the context, specify the eval to wait for contents inside the iframe.

this could be a myVar !== null - more info in the godoc or below.

// IframeAction 
type IframeAction struct {
  Selector string `yaml:"selector,omitempty" json:"selector,omitempty"`
  // WaitEval has to be in the form of a boolean return
  // e.g. `myVar !== null` or `(myVar !== null || document.title == "ready")`
  // the supplied value will be appended to an existing
  // `return document.readyState === 'complete' && ${WaitEval};`
  WaitEval string `yaml:"waitEval,omitempty" json:"waitEval,omitempty"`
}
elementActions

list of actions to perform within the page/view, each elementAction has the following structure

name (required)

Name of the action on the element - will be used in reports

element (required)

the element to identifier - an object with below attrs

selector (required)

CSS or XPath style selector to attempt to locate the element on the page.

if not found and running continueOnError mode the execution will move on to the next element in the sequence.

value (optional)

if value is not provided it will be a click type action, if value is provided it will be an input type action

assert (bool)

Defaults to false.

When running in UI test mode only this should be set to true...

When set to true elements presence is only asserted and any input/click actions will be skipped.

Usage

Download the correct binary for your architecture - instructions

uiseeder -h for help

uiseeder -i path/to/yaml -v

See test/integration.yml for an example with auth you can use with pocketbase.io.

Internals

Currently the entire loop through of Actions is using pointers to allow for an easier report builder output - adding concurency via go routines may be problematic and is not really desired at this point.

Underlying Web Driver

This module and CLI use the Go-Rod which uses the CPD protocol.

Example

With Auth

Most scenarios for UI tests will require a login of sorts, for easy simulation of how this

To run integration style tests you must have the sample app running, you can run it docker locally.

docker run --name=pb-app --detach -p 8090:8090 dnitsch/reststrategy-sample:latest

Then navigate to this page

Add you user name and password and replace it in the YAML with whatever you chose.

Explore any parts of the app and grab elements by XPath or CSS and add new page or element actions within a page.

Help

always wanted and welcomed

  • Still TODO lots more tests
  • report formats and outcomes need fixing up
  • any current/new features...

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type ActionReportItem added in v0.2.1

type ActionReportItem struct {
	Screenshot string   `json:"screenshot"`
	Errored    bool     `json:"errored"`
	Message    string   `json:"message"`
	Output     []string `json:"output"`
}

type ActionsReport added in v0.2.1

type ActionsReport map[string]ActionReportItem

type Auth

type Auth struct {
	Username        Element  `yaml:"username" json:"username"`
	Password        Element  `yaml:"password" json:"password"`
	ConfirmPassword Element  `yaml:"confirmPassword,omitempty" json:"confirmPassword,omitempty"`
	RequireConfirm  bool     `yaml:"requireConfirm,omitempty" json:"requireConfirm,omitempty"`
	Navigate        string   `yaml:"navigate" json:"navigate"`
	IdpManaged      bool     `yaml:"idpManaged" json:"idpManaged"`
	IdpSelector     *Element `yaml:"idpSelector,omitempty" json:"idpSelector,omitempty"`
	MfaSelector     *Element `yaml:"mfaSelector,omitempty" json:"mfaSelector,omitempty"`
	IdpUrl          string   `yaml:"idpUrl" json:"idpUrl"`
	Submit          Element  `yaml:"submit" json:"submit"`
}

type BaseConfig added in v0.1.2

type BaseConfig struct {
	BaseUrl         string     `yaml:"baseUrl" json:"baseUrl"`
	LauncherConfig  *WebConfig `yaml:"browserConfig,omitempty" json:"browserConfig,omitempty"`
	ContinueOnError bool       `yaml:"continueOnError" json:"continueOnError"`
	IsSinglePageApp bool       `yaml:"isSinglePageApp" json:"isSinglePageApp"`
}

BaseConfig is the base config object each web session will have its own go routine to run the entire session Auth -> LoggedInPage ->[]Actions

type Element

type Element struct {
	// Selector can be a CSSStyle selector or XPath
	Selector *string `yaml:"selector,omitempty" json:"selector,omitempty"`
	Value    *string `yaml:"value,omitempty" json:"value,omitempty"`
	Timeout  int
}

type ElementAction added in v0.1.2

type ElementAction struct {
	Name               string  `yaml:"name" json:"name"`
	Element            Element `yaml:"element" json:"element"`
	Assert             bool    `yaml:"assert,omitempty" json:"assert,omitempty"`
	SkipOnErrorMessage string  `yaml:"skipOnErrorMessage,omitempty" json:"skipOnErrorMessage,omitempty"`
	CaptureOutput      bool    `yaml:"captureOutput,omitempty" json:"captureOutput,omitempty"`
	// contains filtered or unexported fields
}

ElementAction

type IframeAction added in v0.1.2

type IframeAction struct {
	Selector string `yaml:"selector,omitempty" json:"selector,omitempty"`
	// WaitEval has to be in the form of a boolean return
	// e.g. `myVar !== null` or `(myVar !== null || document.title == "ready")`
	// the supplied value will be appended to an existing
	// `return document.readyState === 'complete' && ${WaitEval};`
	WaitEval string `yaml:"waitEval,omitempty" json:"waitEval,omitempty"`
}

IframeAction

type LoggedInPage

type LoggedInPage struct {
	*Web
	// contains filtered or unexported fields
}

func (*LoggedInPage) DetermineActionElement

func (lp *LoggedInPage) DetermineActionElement(page *rod.Page, action *ElementAction) (*rod.Element, error)

DetermineActionType returns the rod.Element with correct action

func (*LoggedInPage) DetermineActionType

func (lp *LoggedInPage) DetermineActionType(action *ElementAction, elem *rod.Element) error

DetermineActionType returns the rod.Element with correct action either Click/Swipe or Input when Input is selected - ensure you have specified the input HTML element as the enclosing elements may not always allow for input...

func (*LoggedInPage) PerformActions added in v0.1.2

func (lp *LoggedInPage) PerformActions(action *ViewAction) error

PerformAction handles a single action on Navigate'd page/view of SPA

type UIStrategyError added in v0.2.2

type UIStrategyError struct {
	// contains filtered or unexported fields
}

UIStrategyError custom error handler TODO: enable mutex lock on this just in case

func (*UIStrategyError) Error added in v0.2.2

func (e *UIStrategyError) Error() string

type UiStrategyConf

type UiStrategyConf struct {
	Setup BaseConfig `yaml:"setup" json:"setup"`
	// Auth is optional
	// should be omitted for apps that do not require a login
	Auth    *Auth         `yaml:"auth,omitempty" json:"auth,omitempty"`
	Actions []*ViewAction `yaml:"actions" json:"actions"`
}

type ViewAction added in v0.1.2

type ViewAction struct {
	Iframe         *IframeAction    `yaml:"iframe,omitempty" json:"iframe,omitempty"`
	Name           string           `yaml:"name" json:"name"`
	Navigate       string           `yaml:"navigate" json:"navigate"`
	ElementActions []*ElementAction `yaml:"elementActions" json:"elementActions"`
	// contains filtered or unexported fields
}

ViewAction defines a single action to do e.g. look up item, input text, click/swipe can include Assertion that action successfully occured

func (*ViewAction) WithNavigate added in v0.1.2

func (a *ViewAction) WithNavigate(baseUrl string) *ViewAction

WithNavigate correctly formats the Navigate URL to include the full baseUrl

type ViewReport added in v0.1.2

type ViewReport map[string]ViewReportItem

type ViewReportItem added in v0.2.1

type ViewReportItem struct {
	Message string        `json:"message"`
	Actions ActionsReport `json:"actions"`
}

type Web

type Web struct {
	// contains filtered or unexported fields
}

Web is the single instance struct

func New

func New(conf BaseConfig) *Web

New returns an initialised instance of Web struct with the provided BaseConfig

func (*Web) DoAuth

func (web *Web) DoAuth(auth *Auth) (*LoggedInPage, error)

DoAuth performs the required Authentication in the browser and returns a authed Page

func (*Web) Drive added in v0.1.2

func (web *Web) Drive(ctx context.Context, auth *Auth, allActions []*ViewAction) error

Drive runs a single UIStrategy in the same logged in session returns a custom error type with details of errors per action

func (*Web) WithLogger

func (w *Web) WithLogger(l log.Logger) *Web

WithLogger

type WebConfig added in v0.1.2

type WebConfig struct {
	Headless bool `yaml:"headless" json:"headless"`
	// if enabled it will store session data on disk
	// when used in CI, if you also want this enabled
	// you should also cache the default location of where the cache is:
	// ~/.uistratetegy-data
	PersistSessionOnDisk bool `yaml:"persist" json:"persist"`
	// Timeout will initialises a copy of the page with a context Timeout
	Timeout           int    `yaml:"timeout" json:"timeout"`
	BrowserPathExec   string `yaml:"execPath" json:"execPath"`
	UserMode          bool   `yaml:"userMode" json:"userMode"`
	DataDir           string `yaml:"dataDir" json:"dataDir"`
	ReuseBrowserCache bool   `yaml:"reuseBrowserCache" json:"reuseBrowserCache"`
	NoSandbox         bool   `yaml:"noSandbox" json:"noSandbox"`
}

Directories

Path Synopsis
cmd
internal

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL