From 62ebf85b96ea570c94aef8997c35dad65082e369 Mon Sep 17 00:00:00 2001 From: Daniel Sami Date: Mon, 14 Jan 2019 19:18:47 -0500 Subject: [PATCH 1/4] Documentation of functions --- awx/ui/test/e2e/README.md | 9 ++ awx/ui/test/e2e/commands/findThenClick.js | 4 + awx/ui/test/e2e/commands/waitForAngular.js | 1 + awx/ui/test/e2e/fixtures.js | 99 ++++++++++++++++++++++ 4 files changed, 113 insertions(+) diff --git a/awx/ui/test/e2e/README.md b/awx/ui/test/e2e/README.md index f7a9a2e65b..718708b92b 100644 --- a/awx/ui/test/e2e/README.md +++ b/awx/ui/test/e2e/README.md @@ -43,3 +43,12 @@ npm --prefix awx/ui run e2e -- --filter="test-credentials*" **Note:** - Use `npm --prefix awx/ui run e2e -- --help` to see additional usage information for the test runner. - All example commands in this document assume that you are working from the root directory of the awx project. + +#### File Overview +All nightwatch.js tests are present in the `tests` directory. When writing +these tests, you may import needed functions from [fixtures.js](fixtures.js), which provides a convenient way to create resources needed for tests +via API, which might include organizations, users, and job templates. + +The `commands` directory provides extra functions for the client object in +nightwatch.js tests. For more information on these functions and how to +create your own, refer to the nightwatch.js documentation on custom commands. diff --git a/awx/ui/test/e2e/commands/findThenClick.js b/awx/ui/test/e2e/commands/findThenClick.js index fd8581623e..558ec26944 100644 --- a/awx/ui/test/e2e/commands/findThenClick.js +++ b/awx/ui/test/e2e/commands/findThenClick.js @@ -1,5 +1,9 @@ const spinny = "//*[contains(@class, 'spinny')]"; +/* Utility function for clicking elements; attempts to scroll to + * the element if necessary, and waits for the page to finish loading. + * + * @param selector - xpath of the element to click. */ exports.command = function findThenClick (selector) { this.waitForElementPresent(selector, () => { this.moveToElement(selector, 0, 0, () => { diff --git a/awx/ui/test/e2e/commands/waitForAngular.js b/awx/ui/test/e2e/commands/waitForAngular.js index bad16b09cf..4daf737dea 100644 --- a/awx/ui/test/e2e/commands/waitForAngular.js +++ b/awx/ui/test/e2e/commands/waitForAngular.js @@ -1,5 +1,6 @@ import { AWX_E2E_TIMEOUT_ASYNC } from '../settings'; +/* Post-login utility function that waits for the application to fully load. */ exports.command = function waitForAngular (callback) { this.timeoutsAsyncScript(AWX_E2E_TIMEOUT_ASYNC, () => { this.executeAsync(done => { diff --git a/awx/ui/test/e2e/fixtures.js b/awx/ui/test/e2e/fixtures.js index 56c012fb28..6ace25914d 100644 --- a/awx/ui/test/e2e/fixtures.js +++ b/awx/ui/test/e2e/fixtures.js @@ -10,6 +10,15 @@ import { const session = `e2e-${uuid().substr(0, 8)}`; const store = {}; +/* Utility function for accessing awx resources. This includes resources like + * users, organizations, and job templates. Creates the resource if it does + * not already exist, then returns it. + * + * @param endpoint - The REST API url suffix. + * @param data - Attributes used to create a new endpoint. + * @param [unique=['name']] - A unique key for a resource. + * + */ const getOrCreate = (endpoint, data, unique = ['name']) => { const identifiers = Object.keys(data).filter(key => unique.indexOf(key) > -1); @@ -40,11 +49,22 @@ const getOrCreate = (endpoint, data, unique = ['name']) => { return store[lookup].then(created => created.data); }; +/* Creates an organization if one does not already exist, and returns it. + * + * @param [namespace=session] - A unique name prefix for the organization. + * + */ const getOrganization = (namespace = session) => getOrCreate('/organizations/', { name: `${namespace}-organization`, description: namespace }); +/* Creates an inventory if one does not already exist, and returns it. + * Also creates an organization with the same name prefix if needed. + * + * @param [namespace=session] - A unique name prefix for the inventory. + * + */ const getInventory = (namespace = session) => getOrganization(namespace) .then(organization => getOrCreate('/inventories/', { name: `${namespace}-inventory`, @@ -57,6 +77,10 @@ const getInventory = (namespace = session) => getOrganization(namespace) variables: JSON.stringify({ ansible_connection: 'local' }), }, ['name', 'inventory']).then(() => inventory))); +/* Identical to getInventory except it provides a unique suffix, "*-inventory-nosource". + * + * @param[namespace=session] - A unique name prefix for the inventory. +*/ const getInventoryNoSource = (namespace = session) => getOrganization(namespace) .then(organization => getOrCreate('/inventories/', { name: `${namespace}-inventory-nosource`, @@ -69,6 +93,11 @@ const getInventoryNoSource = (namespace = session) => getOrganization(namespace) variables: JSON.stringify({ ansible_connection: 'local' }), }, ['name', 'inventory']).then(() => inventory))); +/* Gets or, if it does not exist, creates an host with the given name prefix. + * If an inventory does not exist with the same prefix, it is created as well. + * + * @param[namespace=session] - A unique name prefix for the host. + */ const getHost = (namespace = session) => getInventory(namespace) .then(inventory => getOrCreate('/hosts/', { name: `${namespace}-host`, @@ -77,6 +106,12 @@ const getHost = (namespace = session) => getInventory(namespace) variables: JSON.stringify({ ansible_connection: 'local' }), }, ['name', 'inventory'])); +/* Gets or, if it does not exist, creates an inventory script with the given + * name prefix. If an organization does not exist with the same prefix, it is + * created as well. + * + * @param[namespace=session] - A unique name prefix for the host. + */ const getInventoryScript = (namespace = session) => getOrganization(namespace) .then(organization => getOrCreate('/inventory_scripts/', { name: `${namespace}-inventory-script`, @@ -145,6 +180,12 @@ const getAdminMachineCredential = (namespace = session) => { }); }; +/* Gets or, if it does not exist, creates a team with the given + * name prefix. If an organization does not exist with the same prefix, it is + * created as well. + * + * @param[namespace=session] - A unique name prefix for the team. + */ const getTeam = (namespace = session) => getOrganization(namespace) .then(organization => getOrCreate(`/organizations/${organization.id}/teams/`, { name: `${namespace}-team`, @@ -152,6 +193,12 @@ const getTeam = (namespace = session) => getOrganization(namespace) organization: organization.id, })); +/* Gets or, if it does not exist, creates a smart inventory with the given + * name prefix. If an organization does not exist with the same prefix, it is + * created as well. + * + * @param[namespace=session] - A unique name prefix for the smart inventory. + */ const getSmartInventory = (namespace = session) => getOrganization(namespace) .then(organization => getOrCreate('/inventories/', { name: `${namespace}-smart-inventory`, @@ -161,6 +208,12 @@ const getSmartInventory = (namespace = session) => getOrganization(namespace) kind: 'smart' })); +/* Gets or, if it does not exist, creates a notification template with the given + * name prefix. If an organization does not exist with the same prefix, it is + * created as well. + * + * @param[namespace=session] - A unique name prefix for the notification template. + */ const getNotificationTemplate = (namespace = session) => getOrganization(namespace) .then(organization => getOrCreate(`/organizations/${organization.id}/notification_templates/`, { name: `${namespace}-notification-template`, @@ -173,6 +226,12 @@ const getNotificationTemplate = (namespace = session) => getOrganization(namespa } })); +/* Gets or, if it does not exist, creates a project with the given + * name prefix. If an organization does not exist with the same prefix, it is + * created as well. + * + * @param[namespace=session] - A unique name prefix for the host. + */ const getProject = (namespace = session) => getOrganization(namespace) .then(organization => getOrCreate(`/organizations/${organization.id}/projects/`, { name: `${namespace}-project`, @@ -218,6 +277,12 @@ const getUpdatedProject = (namespace = session) => getProject(namespace) return project; }); +/* Gets or, if it does not exist, creates a job template with the given + * name prefix. This function also runs getOrCreate for an inventory, + * credential, and project with the same prefix. + * + * @param[namespace=session] - A unique name prefix for the job template. + */ const getJobTemplate = (namespace = session) => { const promises = [ getInventory(namespace), @@ -236,6 +301,10 @@ const getJobTemplate = (namespace = session) => { })); }; +/* Similar to getJobTemplate, except that it also launches the job. + * + * @param[namespace=session] - A unique name prefix for the host. + */ const getJob = (namespace = session) => getJobTemplate(namespace) .then(template => { const launchURL = template.related.launch; @@ -245,6 +314,12 @@ const getJob = (namespace = session) => getJobTemplate(namespace) }); }); +/* Gets or, if it does not exist, creates a workflow template with the given + * name prefix. If an organization does not exist with the same prefix, it is + * created as well. A basic workflow node setup is also created. + * + * @param[namespace=session] - A unique name prefix for the workflow template. + */ const getWorkflowTemplate = (namespace = session) => { const workflowTemplatePromise = getOrganization(namespace) .then(organization => getOrCreate(`/organizations/${organization.id}/workflow_job_templates/`, { @@ -285,6 +360,12 @@ const getWorkflowTemplate = (namespace = session) => { .then(([workflowTemplate, nodes]) => workflowTemplate); }; +/* Gets or, if it does not exist, creates a auditor user with the given + * name prefix. If an organization does not exist with the same prefix, + * it is also created. + * + * @param[namespace=session] - A unique name prefix for the project admin. + */ const getAuditor = (namespace = session) => getOrganization(namespace) .then(organization => getOrCreate(`/organizations/${organization.id}/users/`, { username: `auditor-${uuid().substr(0, 8)}`, @@ -321,6 +402,12 @@ const getUserExact = (namespace = session, name) => getOrganization(namespace) password: AWX_E2E_PASSWORD }, ['username'])); +/* Gets or, if it does not exist, creates a job template admin with the given + * name prefix. If a job template or organization does not exist with the same + * prefix, they are also created. + * + * @param[namespace=session] - A unique name prefix for the project admin. + */ const getJobTemplateAdmin = (namespace = session) => { const rolePromise = getJobTemplate(namespace) .then(obj => obj.summary_fields.object_roles.admin_role); @@ -344,6 +431,12 @@ const getJobTemplateAdmin = (namespace = session) => { .then(([user, assignment]) => user); }; +/* Gets or, if it does not exist, creates a project admin with the given + * name prefix. If a project or organization does not exist with the same + * prefix, they are also created. + * + * @param[namespace=session] - A unique name prefix for the project admin. + */ const getProjectAdmin = (namespace = session) => { const rolePromise = getUpdatedProject(namespace) .then(obj => obj.summary_fields.object_roles.admin_role); @@ -374,6 +467,12 @@ const getInventorySourceSchedule = (namespace = session) => getInventorySource(n rrule: 'DTSTART:20171104T040000Z RRULE:FREQ=DAILY;INTERVAL=1;COUNT=1' })); +/* Gets or, if it does not exist, creates a job template schedule with the given + * name prefix. If a job template does not exist with the same prefix, it is + * created as well. + * + * @param[namespace=session] - A unique name prefix for the schedule. + */ const getJobTemplateSchedule = (namespace = session) => getJobTemplate(namespace) .then(template => getOrCreate(template.related.schedules, { name: `${template.name}-schedule`, From 0c250cd6afa231d758f7c9f61d94cff62393d05a Mon Sep 17 00:00:00 2001 From: Daniel Sami Date: Wed, 16 Jan 2019 10:44:22 +0900 Subject: [PATCH 2/4] Updated parameter info --- awx/ui/test/e2e/fixtures.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/ui/test/e2e/fixtures.js b/awx/ui/test/e2e/fixtures.js index 6ace25914d..8e3bbbe140 100644 --- a/awx/ui/test/e2e/fixtures.js +++ b/awx/ui/test/e2e/fixtures.js @@ -16,7 +16,7 @@ const store = {}; * * @param endpoint - The REST API url suffix. * @param data - Attributes used to create a new endpoint. - * @param [unique=['name']] - A unique key for a resource. + * @param [unique=['name']] - An array of keys used to uniquely identify previously created resources from the endpoint. * */ const getOrCreate = (endpoint, data, unique = ['name']) => { From ae3ab895153b814dfe5f099b5f57b058c71e6eaa Mon Sep 17 00:00:00 2001 From: Daniel Sami Date: Wed, 16 Jan 2019 11:06:25 +0900 Subject: [PATCH 3/4] lint fix --- awx/ui/test/e2e/fixtures.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/awx/ui/test/e2e/fixtures.js b/awx/ui/test/e2e/fixtures.js index 8e3bbbe140..bfc02e1f8a 100644 --- a/awx/ui/test/e2e/fixtures.js +++ b/awx/ui/test/e2e/fixtures.js @@ -16,7 +16,8 @@ const store = {}; * * @param endpoint - The REST API url suffix. * @param data - Attributes used to create a new endpoint. - * @param [unique=['name']] - An array of keys used to uniquely identify previously created resources from the endpoint. + * @param [unique=['name']] - An array of keys used to uniquely identify previously + * created resources from the endpoint. * */ const getOrCreate = (endpoint, data, unique = ['name']) => { From 7258a43bad7023bd351d9a70d63afbd36cc651b0 Mon Sep 17 00:00:00 2001 From: Daniel Sami Date: Thu, 17 Jan 2019 08:28:22 +0900 Subject: [PATCH 4/4] rewording, typo corrections --- awx/ui/test/e2e/README.md | 6 ++- awx/ui/test/e2e/fixtures.js | 75 ++++++++++++++++++++++++------------- 2 files changed, 54 insertions(+), 27 deletions(-) diff --git a/awx/ui/test/e2e/README.md b/awx/ui/test/e2e/README.md index 718708b92b..b0ac5ba1a0 100644 --- a/awx/ui/test/e2e/README.md +++ b/awx/ui/test/e2e/README.md @@ -50,5 +50,7 @@ these tests, you may import needed functions from [fixtures.js](fixtures.js), wh via API, which might include organizations, users, and job templates. The `commands` directory provides extra functions for the client object in -nightwatch.js tests. For more information on these functions and how to -create your own, refer to the nightwatch.js documentation on custom commands. +nightwatch.js tests. These functions are automatically made available for use by the +client object. For more information on these functions and how to +create your own, refer to the [nightwatch.js documentation on custom commands] +(http://nightwatchjs.org/guide/#writing-custom-commands). diff --git a/awx/ui/test/e2e/fixtures.js b/awx/ui/test/e2e/fixtures.js index bfc02e1f8a..8130a32133 100644 --- a/awx/ui/test/e2e/fixtures.js +++ b/awx/ui/test/e2e/fixtures.js @@ -11,8 +11,8 @@ const session = `e2e-${uuid().substr(0, 8)}`; const store = {}; /* Utility function for accessing awx resources. This includes resources like - * users, organizations, and job templates. Creates the resource if it does - * not already exist, then returns it. + * users, organizations, and job templates. Retrieves the end point, and creates + * it if it does not exist. * * @param endpoint - The REST API url suffix. * @param data - Attributes used to create a new endpoint. @@ -50,7 +50,7 @@ const getOrCreate = (endpoint, data, unique = ['name']) => { return store[lookup].then(created => created.data); }; -/* Creates an organization if one does not already exist, and returns it. +/* Retrieves an organization, and creates it if it does not exist. * * @param [namespace=session] - A unique name prefix for the organization. * @@ -60,7 +60,7 @@ const getOrganization = (namespace = session) => getOrCreate('/organizations/', description: namespace }); -/* Creates an inventory if one does not already exist, and returns it. +/* Retrieves an inventory, and creates it if it does not exist. * Also creates an organization with the same name prefix if needed. * * @param [namespace=session] - A unique name prefix for the inventory. @@ -78,7 +78,8 @@ const getInventory = (namespace = session) => getOrganization(namespace) variables: JSON.stringify({ ansible_connection: 'local' }), }, ['name', 'inventory']).then(() => inventory))); -/* Identical to getInventory except it provides a unique suffix, "*-inventory-nosource". +/* Identical to getInventory except it provides a unique suffix, + * "*-inventory-nosource". * * @param[namespace=session] - A unique name prefix for the inventory. */ @@ -94,7 +95,7 @@ const getInventoryNoSource = (namespace = session) => getOrganization(namespace) variables: JSON.stringify({ ansible_connection: 'local' }), }, ['name', 'inventory']).then(() => inventory))); -/* Gets or, if it does not exist, creates an host with the given name prefix. +/* Retrieves a host with the given name prefix, and creates it if it does not exist. * If an inventory does not exist with the same prefix, it is created as well. * * @param[namespace=session] - A unique name prefix for the host. @@ -107,8 +108,8 @@ const getHost = (namespace = session) => getInventory(namespace) variables: JSON.stringify({ ansible_connection: 'local' }), }, ['name', 'inventory'])); -/* Gets or, if it does not exist, creates an inventory script with the given - * name prefix. If an organization does not exist with the same prefix, it is +/* Retrieves an inventory script with the given name prefix, and creates it if it + * does not exist. If an organization does not exist with the same prefix, it is * created as well. * * @param[namespace=session] - A unique name prefix for the host. @@ -121,6 +122,12 @@ const getInventoryScript = (namespace = session) => getOrganization(namespace) script: '#!/usr/bin/env python' })); +/* Retrieves an inventory source, and creates it if it does not exist. If the + * required dependent inventory and inventory script do not exist, they are also + * created. + * + * @param[namespace=session] - A unique name prefix for the inventory source. + */ const getInventorySource = (namespace = session) => { const promises = [ getInventory(namespace), @@ -137,6 +144,10 @@ const getInventorySource = (namespace = session) => { })); }; +/* Retrieves an AWS credential, and creates it if it does not exist. + * + * @param[namespace=session] - A unique name prefix for the AWS credential. + */ const getAdminAWSCredential = (namespace = session) => { const promises = [ get('/me/'), @@ -163,6 +174,10 @@ const getAdminAWSCredential = (namespace = session) => { }); }; +/* Retrieves a machine credential, and creates it if it does not exist. + * + * @param[namespace=session] - A unique name prefix for the machine credential. + */ const getAdminMachineCredential = (namespace = session) => { const promises = [ get('/me/'), @@ -181,8 +196,8 @@ const getAdminMachineCredential = (namespace = session) => { }); }; -/* Gets or, if it does not exist, creates a team with the given - * name prefix. If an organization does not exist with the same prefix, it is +/* Retrieves a team, and creates it if it does not exist. + * If an organization does not exist with the same prefix, it is * created as well. * * @param[namespace=session] - A unique name prefix for the team. @@ -194,7 +209,7 @@ const getTeam = (namespace = session) => getOrganization(namespace) organization: organization.id, })); -/* Gets or, if it does not exist, creates a smart inventory with the given +/* Retrieves a smart inventory, and creates it if it does not exist. * name prefix. If an organization does not exist with the same prefix, it is * created as well. * @@ -209,7 +224,7 @@ const getSmartInventory = (namespace = session) => getOrganization(namespace) kind: 'smart' })); -/* Gets or, if it does not exist, creates a notification template with the given +/* Retrieves a notification template, and creates it if it does not exist. * name prefix. If an organization does not exist with the same prefix, it is * created as well. * @@ -227,7 +242,7 @@ const getNotificationTemplate = (namespace = session) => getOrganization(namespa } })); -/* Gets or, if it does not exist, creates a project with the given +/* Retrieves a project, and creates it if it does not exist. * name prefix. If an organization does not exist with the same prefix, it is * created as well. * @@ -278,7 +293,7 @@ const getUpdatedProject = (namespace = session) => getProject(namespace) return project; }); -/* Gets or, if it does not exist, creates a job template with the given +/* Retrieves a job template, and creates it if it does not exist. * name prefix. This function also runs getOrCreate for an inventory, * credential, and project with the same prefix. * @@ -315,7 +330,7 @@ const getJob = (namespace = session) => getJobTemplate(namespace) }); }); -/* Gets or, if it does not exist, creates a workflow template with the given +/* Retrieves a workflow template, and creates it if it does not exist. * name prefix. If an organization does not exist with the same prefix, it is * created as well. A basic workflow node setup is also created. * @@ -361,11 +376,11 @@ const getWorkflowTemplate = (namespace = session) => { .then(([workflowTemplate, nodes]) => workflowTemplate); }; -/* Gets or, if it does not exist, creates a auditor user with the given +/* Retrieves a auditor user, and creates it if it does not exist. * name prefix. If an organization does not exist with the same prefix, * it is also created. * - * @param[namespace=session] - A unique name prefix for the project admin. + * @param[namespace=session] - A unique name prefix for the auditor. */ const getAuditor = (namespace = session) => getOrganization(namespace) .then(organization => getOrCreate(`/organizations/${organization.id}/users/`, { @@ -379,6 +394,12 @@ const getAuditor = (namespace = session) => getOrganization(namespace) password: AWX_E2E_PASSWORD }, ['username'])); +/* Retrieves a user, and creates it if it does not exist. + * name prefix. If an organization does not exist with the same prefix, + * it is also created. + * + * @param[namespace=session] - A unique name prefix for the user. + */ const getUser = (namespace = session) => getOrganization(namespace) .then(organization => getOrCreate(`/organizations/${organization.id}/users/`, { username: `user-${uuid().substr(0, 8)}`, @@ -403,11 +424,11 @@ const getUserExact = (namespace = session, name) => getOrganization(namespace) password: AWX_E2E_PASSWORD }, ['username'])); -/* Gets or, if it does not exist, creates a job template admin with the given - * name prefix. If a job template or organization does not exist with the same +/* Retrieves a job template admin, and creates it if it does not exist. + * If a job template or organization does not exist with the same * prefix, they are also created. * - * @param[namespace=session] - A unique name prefix for the project admin. + * @param[namespace=session] - A unique name prefix for the template admin. */ const getJobTemplateAdmin = (namespace = session) => { const rolePromise = getJobTemplate(namespace) @@ -432,8 +453,8 @@ const getJobTemplateAdmin = (namespace = session) => { .then(([user, assignment]) => user); }; -/* Gets or, if it does not exist, creates a project admin with the given - * name prefix. If a project or organization does not exist with the same +/* Retrieves a project admin, and creates it if it does not exist. + * If a job template or organization does not exist with the same * prefix, they are also created. * * @param[namespace=session] - A unique name prefix for the project admin. @@ -461,6 +482,11 @@ const getProjectAdmin = (namespace = session) => { .then(([user, assignment]) => user); }; +/* Retrieves a inventory source schedule, and creates it if it does not exist. + * If an inventory source does not exist with the same prefix, it is also created. + * + * @param[namespace=session] - A unique name prefix for the schedule. + */ const getInventorySourceSchedule = (namespace = session) => getInventorySource(namespace) .then(source => getOrCreate(source.related.schedules, { name: `${source.name}-schedule`, @@ -468,9 +494,8 @@ const getInventorySourceSchedule = (namespace = session) => getInventorySource(n rrule: 'DTSTART:20171104T040000Z RRULE:FREQ=DAILY;INTERVAL=1;COUNT=1' })); -/* Gets or, if it does not exist, creates a job template schedule with the given - * name prefix. If a job template does not exist with the same prefix, it is - * created as well. +/* Retrieves a job template schedule, and creates it if it does not exist. + * If an job template does not exist with the same prefix, it is also created. * * @param[namespace=session] - A unique name prefix for the schedule. */