diff --git a/.secrets.baseline b/.secrets.baseline
index a233e4cf..f7145df8 100644
--- a/.secrets.baseline
+++ b/.secrets.baseline
@@ -3,7 +3,7 @@
"files": "^.secrets.baseline$|^.*pgsslrootcert.yml$",
"lines": null
},
- "generated_at": "2020-01-27T19:24:43Z",
+ "generated_at": "2020-02-10T21:40:38Z",
"plugins_used": [
{
"base64_limit": 4.5,
@@ -82,7 +82,7 @@
"hashed_secret": "afc848c316af1a89d49826c5ae9d00ed769415f3",
"is_secret": false,
"is_verified": false,
- "line_number": 32,
+ "line_number": 33,
"type": "Secret Keyword"
}
],
diff --git a/atst/app.py b/atst/app.py
index 05578827..db6a09c7 100644
--- a/atst/app.py
+++ b/atst/app.py
@@ -233,12 +233,18 @@ def make_config(direct_config=None):
config.set("default", "DATABASE_URI", database_uri)
# Assemble REDIS_URI value
+ redis_use_tls = config["default"].getboolean("REDIS_TLS")
redis_uri = "redis{}://{}:{}@{}".format( # pragma: allowlist secret
- ("s" if config["default"].getboolean("REDIS_TLS") else ""),
+ ("s" if redis_use_tls else ""),
(config.get("default", "REDIS_USER") or ""),
(config.get("default", "REDIS_PASSWORD") or ""),
config.get("default", "REDIS_HOST"),
)
+ if redis_use_tls:
+ tls_mode = config.get("default", "REDIS_SSLMODE")
+ tls_mode_str = tls_mode.lower() if tls_mode else "none"
+ redis_uri = f"{redis_uri}/?ssl_cert_reqs={tls_mode_str}"
+
config.set("default", "REDIS_URI", redis_uri)
return map_config(config)
diff --git a/atst/domain/csp/cloud/azure_cloud_provider.py b/atst/domain/csp/cloud/azure_cloud_provider.py
index 1d921fde..425fe649 100644
--- a/atst/domain/csp/cloud/azure_cloud_provider.py
+++ b/atst/domain/csp/cloud/azure_cloud_provider.py
@@ -1,6 +1,5 @@
import json
from secrets import token_urlsafe
-from typing import Any, Dict
from uuid import uuid4
from atst.utils import sha256_hex
@@ -1026,12 +1025,10 @@ class AzureCloudProvider(CloudProviderInterface):
def update_tenant_creds(self, tenant_id, secret: KeyVaultCredentials):
hashed = sha256_hex(tenant_id)
- new_secrets = secret.dict()
curr_secrets = self._source_tenant_creds(tenant_id)
- updated_secrets: Dict[str, Any] = {**curr_secrets.dict(), **new_secrets}
- us = KeyVaultCredentials(**updated_secrets)
- self.set_secret(hashed, json.dumps(us.dict()))
- return us
+ updated_secrets = curr_secrets.merge_credentials(secret)
+ self.set_secret(hashed, json.dumps(updated_secrets.dict()))
+ return updated_secrets
def _source_tenant_creds(self, tenant_id) -> KeyVaultCredentials:
hashed = sha256_hex(tenant_id)
@@ -1060,7 +1057,7 @@ class AzureCloudProvider(CloudProviderInterface):
"timeframe": "Custom",
"timePeriod": {"from": payload.from_date, "to": payload.to_date,},
"dataset": {
- "granularity": "Daily",
+ "granularity": "Monthly",
"aggregation": {"totalCost": {"name": "PreTaxCost", "function": "Sum"}},
"grouping": [{"type": "Dimension", "name": "InvoiceId"}],
},
diff --git a/atst/domain/csp/cloud/mock_cloud_provider.py b/atst/domain/csp/cloud/mock_cloud_provider.py
index 7ec0636f..da1cccd8 100644
--- a/atst/domain/csp/cloud/mock_cloud_provider.py
+++ b/atst/domain/csp/cloud/mock_cloud_provider.py
@@ -1,4 +1,5 @@
from uuid import uuid4
+import pendulum
from .cloud_provider_interface import CloudProviderInterface
from .exceptions import (
@@ -459,15 +460,26 @@ class MockCloudProvider(CloudProviderInterface):
self._maybe_raise(self.UNAUTHORIZED_RATE, self.AUTHORIZATION_EXCEPTION)
object_id = str(uuid4())
+ start_of_month = pendulum.today(tz="utc").start_of("month").replace(tzinfo=None)
+ this_month = start_of_month.to_atom_string()
+ last_month = start_of_month.subtract(months=1).to_atom_string()
+ two_months_ago = start_of_month.subtract(months=2).to_atom_string()
+
properties = CostManagementQueryProperties(
**dict(
columns=[
{"name": "PreTaxCost", "type": "Number"},
- {"name": "UsageDate", "type": "Number"},
+ {"name": "BillingMonth", "type": "Datetime"},
{"name": "InvoiceId", "type": "String"},
{"name": "Currency", "type": "String"},
],
- rows=[],
+ rows=[
+ [1.0, two_months_ago, "", "USD"],
+ [500.0, two_months_ago, "e05009w9sf", "USD"],
+ [50.0, last_month, "", "USD"],
+ [1000.0, last_month, "e0500a4qhw", "USD"],
+ [500.0, this_month, "", "USD"],
+ ],
)
)
diff --git a/atst/domain/csp/cloud/models.py b/atst/domain/csp/cloud/models.py
index 358f7934..25521bb9 100644
--- a/atst/domain/csp/cloud/models.py
+++ b/atst/domain/csp/cloud/models.py
@@ -417,6 +417,15 @@ class KeyVaultCredentials(BaseModel):
return values
+ def merge_credentials(
+ self, new_creds: "KeyVaultCredentials"
+ ) -> "KeyVaultCredentials":
+ updated_creds = {k: v for k, v in new_creds.dict().items() if v}
+ old_creds = self.dict()
+ old_creds.update(updated_creds)
+
+ return KeyVaultCredentials(**old_creds)
+
class SubscriptionCreationCSPPayload(BaseCSPPayload):
display_name: str
diff --git a/atst/domain/csp/reports.py b/atst/domain/csp/reports.py
index 3f9ccbf8..700947f7 100644
--- a/atst/domain/csp/reports.py
+++ b/atst/domain/csp/reports.py
@@ -1,6 +1,6 @@
-from collections import defaultdict
import json
from decimal import Decimal
+import pendulum
def load_fixture_data():
@@ -11,128 +11,25 @@ def load_fixture_data():
class MockReportingProvider:
FIXTURE_SPEND_DATA = load_fixture_data()
- @classmethod
- def get_portfolio_monthly_spending(cls, portfolio):
- """
- returns an array of application and environment spending for the
- portfolio. Applications and their nested environments are sorted in
- alphabetical order by name.
- [
- {
- name
- this_month
- last_month
- total
- environments [
- {
- name
- this_month
- last_month
- total
- }
- ]
- }
- ]
- """
- fixture_apps = cls.FIXTURE_SPEND_DATA.get(portfolio.name, {}).get(
- "applications", []
- )
+def prepare_azure_reporting_data(rows: list):
+ """
+ Returns a dict representing invoiced and estimated funds for a portfolio given
+ a list of rows from CostManagementQueryCSPResult.properties.rows
+ {
+ invoiced: Decimal,
+ estimated: Decimal
+ }
+ """
- for application in portfolio.applications:
- if application.name not in [app["name"] for app in fixture_apps]:
- fixture_apps.append({"name": application.name, "environments": []})
+ estimated = []
+ while rows:
+ if pendulum.parse(rows[-1][1]) >= pendulum.now(tz="utc").start_of("month"):
+ estimated.append(rows.pop())
+ else:
+ break
- return sorted(
- [
- cls._get_application_monthly_totals(portfolio, fixture_app)
- for fixture_app in fixture_apps
- if fixture_app["name"]
- in [application.name for application in portfolio.applications]
- ],
- key=lambda app: app["name"],
- )
-
- @classmethod
- def _get_environment_monthly_totals(cls, environment):
- """
- returns a dictionary that represents spending totals for an environment e.g.
- {
- name
- this_month
- last_month
- total
- }
- """
- return {
- "name": environment["name"],
- "this_month": sum(environment["spending"]["this_month"].values()),
- "last_month": sum(environment["spending"]["last_month"].values()),
- "total": sum(environment["spending"]["total"].values()),
- }
-
- @classmethod
- def _get_application_monthly_totals(cls, portfolio, fixture_app):
- """
- returns a dictionary that represents spending totals for an application
- and its environments e.g.
- {
- name
- this_month
- last_month
- total
- environments: [
- {
- name
- this_month
- last_month
- total
- }
- ]
- }
- """
- application_envs = [
- env
- for env in portfolio.all_environments
- if env.application.name == fixture_app["name"]
- ]
-
- environments = [
- cls._get_environment_monthly_totals(env)
- for env in fixture_app["environments"]
- if env["name"] in [e.name for e in application_envs]
- ]
-
- for env in application_envs:
- if env.name not in [env["name"] for env in environments]:
- environments.append({"name": env.name})
-
- return {
- "name": fixture_app["name"],
- "this_month": sum(env.get("this_month", 0) for env in environments),
- "last_month": sum(env.get("last_month", 0) for env in environments),
- "total": sum(env.get("total", 0) for env in environments),
- "environments": sorted(environments, key=lambda env: env["name"]),
- }
-
- @classmethod
- def get_spending_by_JEDI_clin(cls, portfolio):
- """
- returns an dictionary of spending per JEDI CLIN for a portfolio
- {
- jedi_clin: {
- invoiced
- estimated
- },
- }
- """
- if portfolio.name in cls.FIXTURE_SPEND_DATA:
- CLIN_spend_dict = defaultdict(lambda: defaultdict(Decimal))
- for application in cls.FIXTURE_SPEND_DATA[portfolio.name]["applications"]:
- for environment in application["environments"]:
- for clin, spend in environment["spending"]["this_month"].items():
- CLIN_spend_dict[clin]["estimated"] += Decimal(spend)
- for clin, spend in environment["spending"]["total"].items():
- CLIN_spend_dict[clin]["invoiced"] += Decimal(spend)
- return CLIN_spend_dict
- return {}
+ return dict(
+ invoiced=Decimal(sum([row[0] for row in rows])),
+ estimated=Decimal(sum([row[0] for row in estimated])),
+ )
diff --git a/atst/domain/reports.py b/atst/domain/reports.py
index 99b229e3..fc619649 100644
--- a/atst/domain/reports.py
+++ b/atst/domain/reports.py
@@ -1,12 +1,13 @@
from flask import current_app
-from itertools import groupby
+from atst.domain.csp.cloud.models import (
+ ReportingCSPPayload,
+ CostManagementQueryCSPResult,
+)
+from atst.domain.csp.reports import prepare_azure_reporting_data
+import pendulum
class Reports:
- @classmethod
- def monthly_spending(cls, portfolio):
- return current_app.csp.reports.get_portfolio_monthly_spending(portfolio)
-
@classmethod
def expired_task_orders(cls, portfolio):
return [
@@ -14,31 +15,19 @@ class Reports:
]
@classmethod
- def obligated_funds_by_JEDI_clin(cls, portfolio):
- clin_spending = current_app.csp.reports.get_spending_by_JEDI_clin(portfolio)
- active_clins = portfolio.active_clins
- for jedi_clin, clins in groupby(
- active_clins, key=lambda clin: clin.jedi_clin_type
- ):
- if not clin_spending.get(jedi_clin.name):
- clin_spending[jedi_clin.name] = {}
- clin_spending[jedi_clin.name]["obligated"] = sum(
- clin.obligated_amount for clin in clins
- )
+ def get_portfolio_spending(cls, portfolio):
+ # TODO: Extend this function to make from_date and to_date configurable
+ from_date = pendulum.now().subtract(years=1).add(days=1).format("YYYY-MM-DD")
+ to_date = pendulum.now().format("YYYY-MM-DD")
+ rows = []
- output = []
- for clin in clin_spending.keys():
- invoiced = clin_spending[clin].get("invoiced", 0)
- estimated = clin_spending[clin].get("estimated", 0)
- obligated = clin_spending[clin].get("obligated", 0)
- remaining = obligated - (invoiced + estimated)
- output.append(
- {
- "name": clin,
- "invoiced": invoiced,
- "estimated": estimated,
- "obligated": obligated,
- "remaining": remaining,
- }
+ if portfolio.csp_data:
+ payload = ReportingCSPPayload(
+ from_date=from_date, to_date=to_date, **portfolio.csp_data
)
- return output
+ response: CostManagementQueryCSPResult = current_app.csp.cloud.get_reporting_data(
+ payload
+ )
+ rows = response.properties.rows
+
+ return prepare_azure_reporting_data(rows)
diff --git a/atst/filters.py b/atst/filters.py
index 3508f1e9..84191017 100644
--- a/atst/filters.py
+++ b/atst/filters.py
@@ -5,7 +5,7 @@ from flask import render_template
from jinja2 import contextfilter
from jinja2.exceptions import TemplateNotFound
from urllib.parse import urlparse, urlunparse, parse_qs, urlencode
-from decimal import DivisionByZero as DivisionByZeroException
+from decimal import DivisionByZero as DivisionByZeroException, InvalidOperation
def iconSvg(name):
@@ -43,7 +43,7 @@ def obligatedFundingGraphWidth(values):
numerator, denominator = values
try:
return (numerator / denominator) * 100
- except DivisionByZeroException:
+ except (DivisionByZeroException, InvalidOperation):
return 0
diff --git a/atst/models/portfolio.py b/atst/models/portfolio.py
index 5a8f0f1e..2ddcaa41 100644
--- a/atst/models/portfolio.py
+++ b/atst/models/portfolio.py
@@ -89,6 +89,12 @@ class Portfolio(
def active_task_orders(self):
return [task_order for task_order in self.task_orders if task_order.is_active]
+ @property
+ def total_obligated_funds(self):
+ return sum(
+ (task_order.total_obligated_funds for task_order in self.active_task_orders)
+ )
+
@property
def funding_duration(self):
"""
diff --git a/atst/routes/portfolios/index.py b/atst/routes/portfolios/index.py
index f9e7d5cf..795d4b70 100644
--- a/atst/routes/portfolios/index.py
+++ b/atst/routes/portfolios/index.py
@@ -34,25 +34,25 @@ def create_portfolio():
@user_can(Permissions.VIEW_PORTFOLIO_REPORTS, message="view portfolio reports")
def reports(portfolio_id):
portfolio = Portfolios.get(g.current_user, portfolio_id)
+ spending = Reports.get_portfolio_spending(portfolio)
+ obligated = portfolio.total_obligated_funds
+ remaining = obligated - (spending["invoiced"] + spending["estimated"])
- current_obligated_funds = Reports.obligated_funds_by_JEDI_clin(portfolio)
+ current_obligated_funds = {
+ **spending,
+ "obligated": obligated,
+ "remaining": remaining,
+ }
- if any(map(lambda clin: clin["remaining"] < 0, current_obligated_funds)):
+ if current_obligated_funds["remaining"] < 0:
flash("insufficient_funds")
- # wrapped in str() because the sum of obligated funds returns a Decimal object
- total_portfolio_value = str(
- sum(
- task_order.total_obligated_funds
- for task_order in portfolio.active_task_orders
- )
- )
return render_template(
"portfolios/reports/index.html",
portfolio=portfolio,
- total_portfolio_value=total_portfolio_value,
+ # wrapped in str() because the sum of obligated funds returns a Decimal object
+ total_portfolio_value=str(portfolio.total_obligated_funds),
current_obligated_funds=current_obligated_funds,
expired_task_orders=Reports.expired_task_orders(portfolio),
- monthly_spending=Reports.monthly_spending(portfolio),
retrieved=datetime.now(), # mocked datetime of reporting data retrival
)
diff --git a/config/base.ini b/config/base.ini
index 1f4c732a..55482741 100644
--- a/config/base.ini
+++ b/config/base.ini
@@ -38,6 +38,7 @@ PGUSER = postgres
PORT=8000
REDIS_HOST=localhost:6379
REDIS_PASSWORD
+REDIS_SSLMODE
REDIS_TLS=False
REDIS_USER
SECRET_KEY = change_me_into_something_secret
diff --git a/js/components/sidenav_toggler.js b/js/components/sidenav_toggler.js
index 11717849..d545b3ed 100644
--- a/js/components/sidenav_toggler.js
+++ b/js/components/sidenav_toggler.js
@@ -1,5 +1,6 @@
import ExpandSidenavMixin from '../mixins/expand_sidenav'
import ToggleMixin from '../mixins/toggle'
+import { sidenavCookieName } from '../lib/constants'
export default {
name: 'sidenav-toggler',
@@ -14,7 +15,7 @@ export default {
toggle: function(e) {
e.preventDefault()
this.isVisible = !this.isVisible
- document.cookie = this.cookieName + '=' + this.isVisible + '; path=/'
+ document.cookie = sidenavCookieName + '=' + this.isVisible + '; path=/'
this.$parent.$emit('sidenavToggle', this.isVisible)
},
},
diff --git a/js/components/toggle_menu.js b/js/components/toggle_menu.js
index e17a201a..6c23ce06 100644
--- a/js/components/toggle_menu.js
+++ b/js/components/toggle_menu.js
@@ -5,6 +5,13 @@ export default {
mixins: [ToggleMixin],
+ props: {
+ defaultVisible: {
+ type: Boolean,
+ default: false,
+ },
+ },
+
methods: {
toggle: function(e) {
if (this.$el.contains(e.target)) {
diff --git a/js/lib/constants.js b/js/lib/constants.js
new file mode 100644
index 00000000..b4de4fcf
--- /dev/null
+++ b/js/lib/constants.js
@@ -0,0 +1 @@
+export const sidenavCookieName = 'expandSidenav'
diff --git a/js/mixins/expand_sidenav.js b/js/mixins/expand_sidenav.js
index 7553b7d4..2af9c87a 100644
--- a/js/mixins/expand_sidenav.js
+++ b/js/mixins/expand_sidenav.js
@@ -1,11 +1,12 @@
+import { sidenavCookieName } from '../lib/constants'
+
export default {
props: {
- cookieName: 'expandSidenav',
defaultVisible: {
type: Boolean,
default: function() {
- if (document.cookie.match(this.cookieName)) {
- return !!document.cookie.match(this.cookieName + ' *= *true')
+ if (document.cookie.match(sidenavCookieName)) {
+ return !!document.cookie.match(sidenavCookieName + ' *= *true')
} else {
return true
}
diff --git a/js/mixins/form.js b/js/mixins/form.js
index 45aa6261..070466de 100644
--- a/js/mixins/form.js
+++ b/js/mixins/form.js
@@ -15,6 +15,7 @@ export default {
return {
changed: this.hasChanges,
valid: false,
+ submitted: false,
}
},
@@ -36,15 +37,16 @@ export default {
handleSubmit: function(event) {
if (!this.valid) {
event.preventDefault()
+ this.submitted = true
}
},
},
computed: {
canSave: function() {
- if (this.changed && this.valid) {
+ if (this.changed && this.valid && !this.submitted) {
return true
- } else if (this.enableSave && this.valid) {
+ } else if (this.enableSave && this.valid && !this.submitted) {
return true
} else {
return false
diff --git a/templates/portfolios/reports/expired_task_orders.html b/templates/portfolios/reports/expired_task_orders.html
index dcde6683..f55bb57e 100644
--- a/templates/portfolios/reports/expired_task_orders.html
+++ b/templates/portfolios/reports/expired_task_orders.html
@@ -16,13 +16,12 @@
PoP |
CLIN Value |
Amount Obligated |
- Amount Unspent |
{% for task_order in expired_task_orders %}
-
+ |
{{ task_order.number }} {{ Icon("caret_right", classes="icon--tiny icon--blue" ) }}
@@ -39,9 +38,8 @@
-
{{ clin.end_date | formattedDate(formatter="%b %d, %Y") }}
|
- {{ clin.total_amount | dollars }} |
- {{ clin.obligated_amount | dollars }} |
- {{ 0 | dollars }} |
+ {{ clin.total_amount | dollars }} |
+ {{ clin.obligated_amount | dollars }} |
{% endfor %}
{% endfor %}
diff --git a/templates/portfolios/reports/index.html b/templates/portfolios/reports/index.html
index 747610d9..51364bf7 100644
--- a/templates/portfolios/reports/index.html
+++ b/templates/portfolios/reports/index.html
@@ -13,7 +13,5 @@
{% include "portfolios/reports/obligated_funds.html" %}
{% include "portfolios/reports/expired_task_orders.html" %}
-
- {% include "portfolios/reports/application_and_env_spending.html" %}
{% endblock %}
diff --git a/templates/portfolios/reports/obligated_funds.html b/templates/portfolios/reports/obligated_funds.html
index e0f11792..0b41b16f 100644
--- a/templates/portfolios/reports/obligated_funds.html
+++ b/templates/portfolios/reports/obligated_funds.html
@@ -7,61 +7,56 @@
- {% for JEDI_clin in current_obligated_funds | sort(attribute='name')%}
-
-
-
-
- {% if JEDI_clin.remaining < 0 %}
-
- {% else %}
- {% set invoiced_width = (JEDI_clin.invoiced, JEDI_clin.obligated) | obligatedFundingGraphWidth %}
- {% if invoiced_width %}
-
-
- {% endif %}
-
- {% set estimated_width = (JEDI_clin.estimated, JEDI_clin.obligated) | obligatedFundingGraphWidth %}
- {% if estimated_width %}
-
-
- {% endif %}
-
+
+
+
+ {% if current_obligated_funds.remaining < 0 %}
+
+ {% else %}
+ {% set invoiced_width = (current_obligated_funds.invoiced, current_obligated_funds.obligated) | obligatedFundingGraphWidth %}
+ {% if invoiced_width %}
+
{% endif %}
-
+ {% set estimated_width = (current_obligated_funds.estimated, current_obligated_funds.obligated) | obligatedFundingGraphWidth %}
+ {% if estimated_width %}
+
+
+ {% endif %}
+
+
+ {% endif %}
+
+
Active Task Orders
diff --git a/tests/domain/cloud/reports/test_reports.py b/tests/domain/cloud/reports/test_reports.py
index 1e74e9b9..91079d44 100644
--- a/tests/domain/cloud/reports/test_reports.py
+++ b/tests/domain/cloud/reports/test_reports.py
@@ -1,58 +1,47 @@
-from atst.domain.csp.reports import MockReportingProvider
+from atst.domain.csp.reports import prepare_azure_reporting_data
from tests.factories import PortfolioFactory
+from decimal import Decimal
+import pendulum
-def test_get_environment_monthly_totals():
- environment = {
- "name": "Test Environment",
- "spending": {
- "this_month": {"JEDI_CLIN_1": 100, "JEDI_CLIN_2": 100},
- "last_month": {"JEDI_CLIN_1": 200, "JEDI_CLIN_2": 200},
- "total": {"JEDI_CLIN_1": 1000, "JEDI_CLIN_2": 1000},
- },
- }
- totals = MockReportingProvider._get_environment_monthly_totals(environment)
- assert totals == {
- "name": "Test Environment",
- "this_month": 200,
- "last_month": 400,
- "total": 2000,
- }
+class TestPrepareAzureData:
+ start_of_month = pendulum.today(tz="utc").start_of("month").replace(tzinfo=None)
+ next_month = start_of_month.add(months=1).to_atom_string()
+ this_month = start_of_month.to_atom_string()
+ last_month = start_of_month.subtract(months=1).to_atom_string()
+ two_months_ago = last_month = start_of_month.subtract(months=2).to_atom_string()
+ def test_estimated_and_invoiced(self):
+ rows = [
+ [150.0, self.two_months_ago, "", "USD"],
+ [100.0, self.last_month, "e0500a4qhw", "USD"],
+ [50.0, self.this_month, "", "USD"],
+ [50.0, self.next_month, "", "USD"],
+ ]
+ output = prepare_azure_reporting_data(rows)
-def test_get_application_monthly_totals():
- portfolio = PortfolioFactory.create(
- applications=[
- {"name": "Test Application", "environments": [{"name": "Z"}, {"name": "A"}]}
- ],
- )
- application = {
- "name": "Test Application",
- "environments": [
- {
- "name": "Z",
- "spending": {
- "this_month": {"JEDI_CLIN_1": 50, "JEDI_CLIN_2": 50},
- "last_month": {"JEDI_CLIN_1": 150, "JEDI_CLIN_2": 150},
- "total": {"JEDI_CLIN_1": 250, "JEDI_CLIN_2": 250},
- },
- },
- {
- "name": "A",
- "spending": {
- "this_month": {"JEDI_CLIN_1": 100, "JEDI_CLIN_2": 100},
- "last_month": {"JEDI_CLIN_1": 200, "JEDI_CLIN_2": 200},
- "total": {"JEDI_CLIN_1": 1000, "JEDI_CLIN_2": 1000},
- },
- },
- ],
- }
+ assert output.get("invoiced") == Decimal(250.0)
+ assert output.get("estimated") == Decimal(100.0)
- totals = MockReportingProvider._get_application_monthly_totals(
- portfolio, application
- )
- assert totals["name"] == "Test Application"
- assert totals["this_month"] == 300
- assert totals["last_month"] == 700
- assert totals["total"] == 2500
- assert [env["name"] for env in totals["environments"]] == ["A", "Z"]
+ def test_just_estimated(self):
+ rows = [
+ [100.0, self.this_month, "", "USD"],
+ ]
+ output = prepare_azure_reporting_data(rows)
+
+ assert output.get("invoiced") == Decimal(0.0)
+ assert output.get("estimated") == Decimal(100.0)
+
+ def test_just_invoiced(self):
+ rows = [
+ [100.0, self.last_month, "", "USD"],
+ ]
+ output = prepare_azure_reporting_data(rows)
+
+ assert output.get("invoiced") == Decimal(100.0)
+ assert output.get("estimated") == Decimal(0.0)
+
+ def test_no_rows(self):
+ output = prepare_azure_reporting_data([])
+ assert output.get("invoiced") == Decimal(0.0)
+ assert output.get("estimated") == Decimal(0.0)
diff --git a/tests/domain/cloud/test_azure_csp.py b/tests/domain/cloud/test_azure_csp.py
index 3a25f849..c89e6a77 100644
--- a/tests/domain/cloud/test_azure_csp.py
+++ b/tests/domain/cloud/test_azure_csp.py
@@ -25,6 +25,7 @@ from atst.domain.csp.cloud.models import (
CostManagementQueryCSPResult,
EnvironmentCSPPayload,
EnvironmentCSPResult,
+ KeyVaultCredentials,
PrincipalAdminRoleCSPPayload,
PrincipalAdminRoleCSPResult,
ProductPurchaseCSPPayload,
@@ -938,3 +939,23 @@ def test_create_user(mock_azure: AzureCloudProvider):
result = mock_azure.create_user(payload)
assert result.id == "id"
+
+
+def test_update_tenant_creds(mock_azure: AzureCloudProvider):
+ with patch.object(
+ AzureCloudProvider, "set_secret", wraps=mock_azure.set_secret,
+ ) as set_secret:
+ set_secret.return_value = None
+ existing_secrets = {
+ "tenant_id": "mytenant",
+ "tenant_admin_username": "admin",
+ "tenant_admin_password": "foo", # pragma: allowlist secret
+ }
+ mock_azure = mock_get_secret(mock_azure, json.dumps(existing_secrets))
+
+ mock_new_secrets = KeyVaultCredentials(**MOCK_CREDS)
+ updated_secret = mock_azure.update_tenant_creds("mytenant", mock_new_secrets)
+
+ assert updated_secret == KeyVaultCredentials(
+ **{**existing_secrets, **MOCK_CREDS}
+ )
diff --git a/tests/domain/cloud/test_models.py b/tests/domain/cloud/test_models.py
index 10c81293..29bc60cb 100644
--- a/tests/domain/cloud/test_models.py
+++ b/tests/domain/cloud/test_models.py
@@ -100,6 +100,26 @@ def test_KeyVaultCredentials_enforce_root_creds():
)
+def test_KeyVaultCredentials_merge_credentials():
+ old_secret = KeyVaultCredentials(
+ tenant_id="foo",
+ tenant_admin_username="bar",
+ tenant_admin_password="baz", # pragma: allowlist secret
+ )
+ new_secret = KeyVaultCredentials(
+ tenant_id="foo", tenant_sp_client_id="bip", tenant_sp_key="bop"
+ )
+
+ expected_update = KeyVaultCredentials(
+ tenant_id="foo",
+ tenant_admin_username="bar",
+ tenant_admin_password="baz", # pragma: allowlist secret
+ tenant_sp_client_id="bip",
+ tenant_sp_key="bop",
+ )
+ assert old_secret.merge_credentials(new_secret) == expected_update
+
+
user_payload = {
"tenant_id": "123",
"display_name": "Han Solo",
diff --git a/tests/domain/test_reports.py b/tests/domain/test_reports.py
index 33ac926e..cdd5de5e 100644
--- a/tests/domain/test_reports.py
+++ b/tests/domain/test_reports.py
@@ -1,8 +1,31 @@
-# TODO: Implement when we get real reporting data
-def test_expired_task_orders():
- pass
+import pytest
+
+from atst.domain.reports import Reports
+from tests.factories import PortfolioFactory
+from decimal import Decimal
-# TODO: Implement when we get real reporting data
-def test_obligated_funds_by_JEDI_clin():
- pass
+@pytest.fixture(scope="function")
+def portfolio():
+ portfolio = PortfolioFactory.create()
+ return portfolio
+
+
+class TestGetPortfolioSpending:
+ csp_data = {
+ "tenant_id": "",
+ "billing_profile_properties": {
+ "invoice_sections": [{"invoice_section_id": "",}]
+ },
+ }
+
+ def test_with_csp_data(self, portfolio):
+ portfolio.csp_data = self.csp_data
+ data = Reports.get_portfolio_spending(portfolio)
+ assert data["invoiced"] == Decimal(1551.0)
+ assert data["estimated"] == Decimal(500.0)
+
+ def test_without_csp_data(self, portfolio):
+ data = Reports.get_portfolio_spending(portfolio)
+ assert data["invoiced"] == Decimal(0)
+ assert data["estimated"] == Decimal(0)
diff --git a/tests/test_app.py b/tests/test_app.py
index 937a15e2..21fd8284 100644
--- a/tests/test_app.py
+++ b/tests/test_app.py
@@ -7,6 +7,7 @@ from atst.app import (
make_crl_validator,
apply_config_from_directory,
apply_config_from_environment,
+ make_config,
)
@@ -67,3 +68,18 @@ def test_apply_config_from_environment_skips_unknown_settings(
monkeypatch.setenv("FLARF", "MAYO")
apply_config_from_environment(config_object)
assert "FLARF" not in config_object.options("default")
+
+
+class TestMakeConfig:
+ def test_redis_ssl_connection(self):
+ config = make_config({"REDIS_TLS": True})
+ uri = config.get("REDIS_URI")
+ assert "rediss" in uri
+ assert "ssl_cert_reqs" in uri
+
+ def test_non_redis_ssl_connection(self):
+ config = make_config({"REDIS_TLS": False})
+ uri = config.get("REDIS_URI")
+ assert "rediss" not in uri
+ assert "redis" in uri
+ assert "ssl_cert_reqs" not in uri
diff --git a/translations.yaml b/translations.yaml
index c16f89c6..b80f5dca 100644
--- a/translations.yaml
+++ b/translations.yaml
@@ -313,7 +313,7 @@ forms:
upload_error: There was an error uploading your file. Please try again. If you encounter repeated problems uploading this file, please contact CCPO.
size_error: "The file you have selected is too large. Please choose a file no larger than {file_size_limit}MB."
filename_error: File names can only contain the characters A-Z, 0-9, space, hyphen, underscore, and period.
- number_description: 13-Digit Task Order Number
+ number_description: Task Order Number
pop_errors:
date_order: PoP start date must be before end date.
range: Date must be between {start} and {end}.
@@ -530,7 +530,7 @@ task_orders:
form:
add_clin: Add Another CLIN
add_to_header: Enter the Task Order number
- add_to_description: Please input your 13-digit Task Order number. This number may be listed under "Order Number" if your Contracting Officer used form 1149, or "Delivery Order/Call No." if form 1155 was used. Moving forward, this portion of funding will be referenced by the recorded Task Order number.
+ add_to_description: Please input your Task Order number. This number may be listed under "Order Number" if your Contracting Officer used form 1149, or "Delivery Order/Call No." if form 1155 was used. Moving forward, this portion of funding will be referenced by the recorded Task Order number.
builder_base:
cancel_modal: Do you want to save this draft?
delete_draft: No, delete it
diff --git a/uitests/Add_CCPO_User.html b/uitests/Add_CCPO_User.html
index e4b43af8..35256565 100644
--- a/uitests/Add_CCPO_User.html
+++ b/uitests/Add_CCPO_User.html
@@ -202,12 +202,12 @@
waitForElementPresent |
-css=.global-panel-container > h3 |
+css=.global-panel-container > .ccpo-panel-container > h3 |
|
assertText |
-css=.global-panel-container > h3 |
+css=.global-panel-container > .ccpo-panel-container > h3 |
*Confirm new CCPO user* |
@@ -277,12 +277,12 @@
waitForElementPresent |
-css=.global-panel-container > div:nth-of-type(2) > .modal > .modal__container > .modal__dialog > .modal__body > div:nth-of-type(2) > .action-group > a.action-group__action.icon-link.icon-link--default |
+css=.global-panel-container > .ccpo-panel-container > div:nth-of-type(2) > .modal > .modal__container > .modal__dialog > .modal__body > div:nth-of-type(2) > .action-group > a.action-group__action.icon-link.icon-link--default |
|
assertElementPresent |
-css=.global-panel-container > div:nth-of-type(2) > .modal > .modal__container > .modal__dialog > .modal__body > div:nth-of-type(2) > .action-group > a.action-group__action.icon-link.icon-link--default |
+css=.global-panel-container > .ccpo-panel-container > div:nth-of-type(2) > .modal > .modal__container > .modal__dialog > .modal__body > div:nth-of-type(2) > .action-group > a.action-group__action.icon-link.icon-link--default |
|
diff --git a/uitests/Application_Index_with_App.html b/uitests/Application_Index_with_App.html
index c7efd48a..6771ba56 100644
--- a/uitests/Application_Index_with_App.html
+++ b/uitests/Application_Index_with_App.html
@@ -237,7 +237,7 @@ Imported from: AT-AT CI - New Portfolio-->
assertText |
css=.empty-state > h3 |
-*You don't have any Applications yet* |
+*You don’t have any Applications yet* |
waitForPageToLoad |
@@ -487,12 +487,12 @@ Imported from: AT-AT CI - New Portfolio-->
waitForElementPresent |
-css=span.action-group-footer > a.usa-button |
+css=.action-group-footer > .action-group-footer--container > a.usa-button |
|
-
+
click |
-css=span.action-group-footer > a.usa-button |
+css=.action-group-footer > .action-group-footer--container > a.usa-button |
|
@@ -535,12 +535,12 @@ Imported from: AT-AT CI - New Portfolio-->
waitForElementPresent |
-css=.accordion-table__items > .accordion-table__item:nth-of-type(1) > .accordion-table__item-content > .environment-list__item > .label.label--below |
+css=.accordion-table__items > .accordion-table__item:nth-of-type(1) > .accordion-table__item-content > .environment-list__item > .label |
|
assertElementPresent |
-css=.accordion-table__items > .accordion-table__item:nth-of-type(1) > .accordion-table__item-content > .environment-list__item > .label.label--below |
+css=.accordion-table__items > .accordion-table__item:nth-of-type(1) > .accordion-table__item-content > .environment-list__item > .label |
|
diff --git a/uitests/Create_New_Application.html b/uitests/Create_New_Application.html
index 564c1886..43d4cb0d 100644
--- a/uitests/Create_New_Application.html
+++ b/uitests/Create_New_Application.html
@@ -224,7 +224,7 @@ Imported from: AT-AT CI - login-->
assertText |
css=.empty-state > h3 |
-*You don't have any Applications yet* |
+*You don’t have any Applications yet* |
waitForPageToLoad |
@@ -458,12 +458,12 @@ Imported from: AT-AT CI - login-->
waitForElementPresent |
-css=span.action-group-footer > a.usa-button |
+css=.action-group-footer > .action-group-footer--container > a.usa-button |
|
-
+
click |
-css=span.action-group-footer > a.usa-button |
+css=.action-group-footer > .action-group-footer--container > a.usa-button |
|
@@ -503,12 +503,12 @@ Imported from: AT-AT CI - login-->
waitForElementPresent |
-css=.accordion-table__items > .accordion-table__item:nth-of-type(1) > .accordion-table__item-content > .environment-list__item > .label.label--below |
+css=.accordion-table__items > .accordion-table__item:nth-of-type(1) > .accordion-table__item-content > .environment-list__item > .label |
|
assertElementPresent |
-css=.accordion-table__items > .accordion-table__item:nth-of-type(1) > .accordion-table__item-content > .environment-list__item > .label.label--below |
+css=.accordion-table__items > .accordion-table__item:nth-of-type(1) > .accordion-table__item-content > .environment-list__item > .label |
|
diff --git a/uitests/Create_New_TO.html b/uitests/Create_New_TO.html
index 1d85c2bc..a9615fa4 100644
--- a/uitests/Create_New_TO.html
+++ b/uitests/Create_New_TO.html
@@ -165,7 +165,7 @@ Imported from: AT-AT CI - login-->
assertText |
css=.empty-state > h3 |
-*You don't have any Applications yet* |
+*You don’t have any Applications yet* |
waitForPageToLoad |
diff --git a/uitests/Edit_App_Member.html b/uitests/Edit_App_Member.html
index e4e92fa8..bd65abb2 100644
--- a/uitests/Edit_App_Member.html
+++ b/uitests/Edit_App_Member.html
@@ -237,7 +237,7 @@ Imported from: AT-AT CI - New Portfolio-->
assertText |
css=.empty-state > h3 |
-*You don't have any Applications yet* |
+*You don’t have any Applications yet* |
waitForPageToLoad |
@@ -487,12 +487,12 @@ Imported from: AT-AT CI - New Portfolio-->
waitForElementPresent |
-css=span.action-group-footer > a.usa-button |
+css=.action-group-footer > .action-group-footer--container > a.usa-button |
|
-
+
click |
-css=span.action-group-footer > a.usa-button |
+css=.action-group-footer > .action-group-footer--container > a.usa-button |
|
@@ -535,12 +535,12 @@ Imported from: AT-AT CI - New Portfolio-->
waitForElementPresent |
-css=.accordion-table__items > .accordion-table__item:nth-of-type(1) > .accordion-table__item-content > .environment-list__item > .label.label--below |
+css=.accordion-table__items > .accordion-table__item:nth-of-type(1) > .accordion-table__item-content > .environment-list__item > .label |
|
assertElementPresent |
-css=.accordion-table__items > .accordion-table__item:nth-of-type(1) > .accordion-table__item-content > .environment-list__item > .label.label--below |
+css=.accordion-table__items > .accordion-table__item:nth-of-type(1) > .accordion-table__item-content > .environment-list__item > .label |
|
diff --git a/uitests/Edit_Portfolio_Member.html b/uitests/Edit_Portfolio_Member.html
index 3fd58cee..c1999ccd 100644
--- a/uitests/Edit_Portfolio_Member.html
+++ b/uitests/Edit_Portfolio_Member.html
@@ -246,7 +246,7 @@ Imported from: AT-AT CI - New Portfolio-->
assertText |
css=.empty-state > h3 |
-*You don't have any Applications yet* |
+*You don’t have any Applications yet* |
waitForPageToLoad |
@@ -308,12 +308,12 @@ Imported from: AT-AT CI - Portfolio Settings-->
Imported from: AT-AT CI - Portfolio Settings-->
waitForElementPresent |
-css=button.usa-button.usa-button-primary.usa-button-big |
+css=button.usa-button.usa-button-primary |
|
assertText |
-css=button.usa-button.usa-button-primary.usa-button-big |
+css=button.usa-button.usa-button-primary |
Save Changes |
@@ -595,12 +595,12 @@ Brandon Buchannan's access to this Portfolio is pending until they sign in for t
waitForElementPresent |
-css=.portfolio-content > div:nth-of-type(3) > .modal.form-content--app-mem > .modal__container > .modal__dialog > .modal__body > .modal__form--header > h1 |
+css=.portfolio-admin > div:nth-of-type(2) > .modal.form-content--app-mem > .modal__container > .modal__dialog > .modal__body > .modal__form--header > h1 |
|
assertElementPresent |
-css=.portfolio-content > div:nth-of-type(3) > .modal.form-content--app-mem > .modal__container > .modal__dialog > .modal__body > .modal__form--header > h1 |
+css=.portfolio-admin > div:nth-of-type(2) > .modal.form-content--app-mem > .modal__container > .modal__dialog > .modal__body > .modal__form--header > h1 |
|
diff --git a/uitests/New_App_Step_1.html b/uitests/New_App_Step_1.html
index 83f70ab6..f4ce8b57 100644
--- a/uitests/New_App_Step_1.html
+++ b/uitests/New_App_Step_1.html
@@ -165,7 +165,7 @@ Imported from: AT-AT CI - login-->
assertText |
css=.empty-state > h3 |
-*You don't have any Applications yet* |
+*You don’t have any Applications yet* |
waitForPageToLoad |
diff --git a/uitests/New_App_Step_2.html b/uitests/New_App_Step_2.html
index 50338788..e75421a1 100644
--- a/uitests/New_App_Step_2.html
+++ b/uitests/New_App_Step_2.html
@@ -174,7 +174,7 @@ Imported from: AT-AT CI - New Portfolio-->
assertText |
css=.empty-state > h3 |
-*You don't have any Applications yet* |
+*You don’t have any Applications yet* |
waitForPageToLoad |
diff --git a/uitests/New_App_Step_2_-_Add_Env.html b/uitests/New_App_Step_2_-_Add_Env.html
index 7450d2e3..e5130a74 100644
--- a/uitests/New_App_Step_2_-_Add_Env.html
+++ b/uitests/New_App_Step_2_-_Add_Env.html
@@ -183,7 +183,7 @@ Imported from: AT-AT CI - New Portfolio-->
assertText |
css=.empty-state > h3 |
-*You don't have any Applications yet* |
+*You don’t have any Applications yet* |
waitForPageToLoad |
diff --git a/uitests/New_App_Step_3.html b/uitests/New_App_Step_3.html
index f8e4948c..6d17272f 100644
--- a/uitests/New_App_Step_3.html
+++ b/uitests/New_App_Step_3.html
@@ -242,7 +242,7 @@ Imported from: AT-AT CI - New Portfolio-->
assertText |
css=.empty-state > h3 |
-*You don't have any Applications yet* |
+*You don’t have any Applications yet* |
waitForPageToLoad |
diff --git a/uitests/New_Portfolio.html b/uitests/New_Portfolio.html
index cb3f3c26..b75e75ae 100644
--- a/uitests/New_Portfolio.html
+++ b/uitests/New_Portfolio.html
@@ -156,7 +156,7 @@
assertText |
css=.empty-state > h3 |
-*You don't have any Applications yet* |
+*You don’t have any Applications yet* |
diff --git a/uitests/New_Portfolio_Member.html b/uitests/New_Portfolio_Member.html
index ab4480ee..a3c7062e 100644
--- a/uitests/New_Portfolio_Member.html
+++ b/uitests/New_Portfolio_Member.html
@@ -233,7 +233,7 @@ Imported from: AT-AT CI - New Portfolio-->
assertText |
css=.empty-state > h3 |
-*You don't have any Applications yet* |
+*You don’t have any Applications yet* |
waitForPageToLoad |
@@ -291,12 +291,12 @@ Imported from: AT-AT CI - New Portfolio-->
waitForElementPresent |
-css=button.usa-button.usa-button-primary.usa-button-big |
+css=button.usa-button.usa-button-primary |
|
assertText |
-css=button.usa-button.usa-button-primary.usa-button-big |
+css=button.usa-button.usa-button-primary |
Save Changes |
diff --git a/uitests/Portfolio_Settings.html b/uitests/Portfolio_Settings.html
index 4c3ea61d..9782587d 100644
--- a/uitests/Portfolio_Settings.html
+++ b/uitests/Portfolio_Settings.html
@@ -165,7 +165,7 @@ Imported from: AT-AT CI - login-->
assertText |
css=.empty-state > h3 |
-*You don't have any Applications yet* |
+*You don’t have any Applications yet* |
waitForPageToLoad |
@@ -219,12 +219,12 @@ Imported from: AT-AT CI - login-->
waitForElementPresent |
-css=button.usa-button.usa-button-primary.usa-button-big |
+css=button.usa-button.usa-button-primary |
|
assertText |
-css=button.usa-button.usa-button-primary.usa-button-big |
+css=button.usa-button.usa-button-primary |
Save Changes |
diff --git a/uitests/Reports_-_Basics.html b/uitests/Reports_-_Basics.html
index ecb8e962..04e6b387 100644
--- a/uitests/Reports_-_Basics.html
+++ b/uitests/Reports_-_Basics.html
@@ -174,7 +174,7 @@ Imported from: AT-AT CI - New Portfolio-->
assertText |
css=.empty-state > h3 |
-*You don't have any Applications yet* |
+*You don’t have any Applications yet* |
waitForPageToLoad |
diff --git a/uitests/Reports_-_Empty_State.html b/uitests/Reports_-_Empty_State.html
index 448a3cb0..6d3c4988 100644
--- a/uitests/Reports_-_Empty_State.html
+++ b/uitests/Reports_-_Empty_State.html
@@ -165,7 +165,7 @@ Imported from: AT-AT CI - login-->
assertText |
css=.empty-state > h3 |
-*You don't have any Applications yet* |
+*You don’t have any Applications yet* |
waitForPageToLoad |
diff --git a/uitests/Reports_-_Follow_Add_App_Button.html b/uitests/Reports_-_Follow_Add_App_Button.html
index c1607b2f..ebbeaa70 100644
--- a/uitests/Reports_-_Follow_Add_App_Button.html
+++ b/uitests/Reports_-_Follow_Add_App_Button.html
@@ -183,7 +183,7 @@ Imported from: AT-AT CI - New Portfolio-->
assertText |
css=.empty-state > h3 |
-*You don't have any Applications yet* |
+*You don’t have any Applications yet* |
waitForPageToLoad |
diff --git a/uitests/Reports_-_Follow_TO_link.html b/uitests/Reports_-_Follow_TO_link.html
index 7c3d2e6d..91ecd4e3 100644
--- a/uitests/Reports_-_Follow_TO_link.html
+++ b/uitests/Reports_-_Follow_TO_link.html
@@ -183,7 +183,7 @@ Imported from: AT-AT CI - New Portfolio-->
assertText |
css=.empty-state > h3 |
-*You don't have any Applications yet* |
+*You don’t have any Applications yet* |
waitForPageToLoad |
diff --git a/uitests/Reports_-_with_TO,_App,_and_Environments.html b/uitests/Reports_-_with_TO,_App,_and_Environments.html
index 6fbc96e2..1afec10f 100644
--- a/uitests/Reports_-_with_TO,_App,_and_Environments.html
+++ b/uitests/Reports_-_with_TO,_App,_and_Environments.html
@@ -237,7 +237,7 @@ Imported from: AT-AT CI - New Portfolio-->
assertText |
css=.empty-state > h3 |
-*You don't have any Applications yet* |
+*You don’t have any Applications yet* |
waitForPageToLoad |
@@ -487,12 +487,12 @@ Imported from: AT-AT CI - New Portfolio-->
waitForElementPresent |
-css=span.action-group-footer > a.usa-button |
+css=.action-group-footer > .action-group-footer--container > a.usa-button |
|
-
+
click |
-css=span.action-group-footer > a.usa-button |
+css=.action-group-footer > .action-group-footer--container > a.usa-button |
|
@@ -535,12 +535,12 @@ Imported from: AT-AT CI - New Portfolio-->
waitForElementPresent |
-css=.accordion-table__items > .accordion-table__item:nth-of-type(1) > .accordion-table__item-content > .environment-list__item > .label.label--below |
+css=.accordion-table__items > .accordion-table__item:nth-of-type(1) > .accordion-table__item-content > .environment-list__item > .label |
|
assertElementPresent |
-css=.accordion-table__items > .accordion-table__item:nth-of-type(1) > .accordion-table__item-content > .environment-list__item > .label.label--below |
+css=.accordion-table__items > .accordion-table__item:nth-of-type(1) > .accordion-table__item-content > .environment-list__item > .label |
|
diff --git a/uitests/Reports_-_with_expired_TO.html b/uitests/Reports_-_with_expired_TO.html
index df76aa00..297cfba6 100644
--- a/uitests/Reports_-_with_expired_TO.html
+++ b/uitests/Reports_-_with_expired_TO.html
@@ -237,7 +237,7 @@ Imported from: AT-AT CI - New Portfolio-->
assertText |
css=.empty-state > h3 |
-*You don't have any Applications yet* |
+*You don’t have any Applications yet* |
waitForPageToLoad |
@@ -487,12 +487,12 @@ Imported from: AT-AT CI - New Portfolio-->
waitForElementPresent |
-css=span.action-group-footer > a.usa-button |
+css=.action-group-footer > .action-group-footer--container > a.usa-button |
|
-
+
click |
-css=span.action-group-footer > a.usa-button |
+css=.action-group-footer > .action-group-footer--container > a.usa-button |
|
@@ -535,12 +535,12 @@ Imported from: AT-AT CI - New Portfolio-->
waitForElementPresent |
-css=.accordion-table__items > .accordion-table__item:nth-of-type(1) > .accordion-table__item-content > .environment-list__item > .label.label--below |
+css=.accordion-table__items > .accordion-table__item:nth-of-type(1) > .accordion-table__item-content > .environment-list__item > .label |
|
assertElementPresent |
-css=.accordion-table__items > .accordion-table__item:nth-of-type(1) > .accordion-table__item-content > .environment-list__item > .label.label--below |
+css=.accordion-table__items > .accordion-table__item:nth-of-type(1) > .accordion-table__item-content > .environment-list__item > .label |
|
diff --git a/uitests/Resend_App_Member_Invite.html b/uitests/Resend_App_Member_Invite.html
index def438b5..fe718a81 100644
--- a/uitests/Resend_App_Member_Invite.html
+++ b/uitests/Resend_App_Member_Invite.html
@@ -237,7 +237,7 @@ Imported from: AT-AT CI - New Portfolio-->
assertText |
css=.empty-state > h3 |
-*You don't have any Applications yet* |
+*You don’t have any Applications yet* |
waitForPageToLoad |
@@ -487,12 +487,12 @@ Imported from: AT-AT CI - New Portfolio-->
waitForElementPresent |
-css=span.action-group-footer > a.usa-button |
+css=.action-group-footer > .action-group-footer--container > a.usa-button |
|
-
+
click |
-css=span.action-group-footer > a.usa-button |
+css=.action-group-footer > .action-group-footer--container > a.usa-button |
|
@@ -535,12 +535,12 @@ Imported from: AT-AT CI - New Portfolio-->
waitForElementPresent |
-css=.accordion-table__items > .accordion-table__item:nth-of-type(1) > .accordion-table__item-content > .environment-list__item > .label.label--below |
+css=.accordion-table__items > .accordion-table__item:nth-of-type(1) > .accordion-table__item-content > .environment-list__item > .label |
|
assertElementPresent |
-css=.accordion-table__items > .accordion-table__item:nth-of-type(1) > .accordion-table__item-content > .environment-list__item > .label.label--below |
+css=.accordion-table__items > .accordion-table__item:nth-of-type(1) > .accordion-table__item-content > .environment-list__item > .label |
|
diff --git a/uitests/Resend_Portfolio_Member_Invite.html b/uitests/Resend_Portfolio_Member_Invite.html
index b369438f..2f6570af 100644
--- a/uitests/Resend_Portfolio_Member_Invite.html
+++ b/uitests/Resend_Portfolio_Member_Invite.html
@@ -246,7 +246,7 @@ Imported from: AT-AT CI - New Portfolio-->
assertText |
css=.empty-state > h3 |
-*You don't have any Applications yet* |
+*You don’t have any Applications yet* |
waitForPageToLoad |
@@ -308,12 +308,12 @@ Imported from: AT-AT CI - Portfolio Settings-->
Imported from: AT-AT CI - Portfolio Settings-->
waitForElementPresent |
-css=button.usa-button.usa-button-primary.usa-button-big |
+css=button.usa-button.usa-button-primary |
|
assertText |
-css=button.usa-button.usa-button-primary.usa-button-big |
+css=button.usa-button.usa-button-primary |
Save Changes |
@@ -595,12 +595,12 @@ Brandon Buchannan's access to this Portfolio is pending until they sign in for t
waitForElementPresent |
-css=.portfolio-content > div:nth-of-type(4) > .modal.form-content--app-mem > .modal__container > .modal__dialog > .modal__body > .modal__form--header > h1 |
+css=.portfolio-admin > div:nth-of-type(3) > .modal.form-content--app-mem > .modal__container > .modal__dialog > .modal__body > .modal__form--header > h1 |
|
assertText |
-css=.portfolio-content > div:nth-of-type(4) > .modal.form-content--app-mem > .modal__container > .modal__dialog > .modal__body > .modal__form--header > h1 |
+css=.portfolio-admin > div:nth-of-type(3) > .modal.form-content--app-mem > .modal__container > .modal__dialog > .modal__body > .modal__form--header > h1 |
*Verify Member Information* |
diff --git a/uitests/Revoke_App_Member_Invite.html b/uitests/Revoke_App_Member_Invite.html
index 847afe97..8452d19c 100644
--- a/uitests/Revoke_App_Member_Invite.html
+++ b/uitests/Revoke_App_Member_Invite.html
@@ -237,7 +237,7 @@ Imported from: AT-AT CI - New Portfolio-->
assertText |
css=.empty-state > h3 |
-*You don't have any Applications yet* |
+*You don’t have any Applications yet* |
waitForPageToLoad |
@@ -487,12 +487,12 @@ Imported from: AT-AT CI - New Portfolio-->
waitForElementPresent |
-css=span.action-group-footer > a.usa-button |
+css=.action-group-footer > .action-group-footer--container > a.usa-button |
|
-
+
click |
-css=span.action-group-footer > a.usa-button |
+css=.action-group-footer > .action-group-footer--container > a.usa-button |
|
@@ -535,12 +535,12 @@ Imported from: AT-AT CI - New Portfolio-->
waitForElementPresent |
-css=.accordion-table__items > .accordion-table__item:nth-of-type(1) > .accordion-table__item-content > .environment-list__item > .label.label--below |
+css=.accordion-table__items > .accordion-table__item:nth-of-type(1) > .accordion-table__item-content > .environment-list__item > .label |
|
assertElementPresent |
-css=.accordion-table__items > .accordion-table__item:nth-of-type(1) > .accordion-table__item-content > .environment-list__item > .label.label--below |
+css=.accordion-table__items > .accordion-table__item:nth-of-type(1) > .accordion-table__item-content > .environment-list__item > .label |
|
diff --git a/uitests/Revoke_Environment_Access.html b/uitests/Revoke_Environment_Access.html
index 6506733b..a73e40d7 100644
--- a/uitests/Revoke_Environment_Access.html
+++ b/uitests/Revoke_Environment_Access.html
@@ -237,7 +237,7 @@ Imported from: AT-AT CI - New Portfolio-->
assertText |
css=.empty-state > h3 |
-*You don't have any Applications yet* |
+*You don’t have any Applications yet* |
waitForPageToLoad |
@@ -487,12 +487,12 @@ Imported from: AT-AT CI - New Portfolio-->
waitForElementPresent |
-css=span.action-group-footer > a.usa-button |
+css=.action-group-footer > .action-group-footer--container > a.usa-button |
|
-
+
click |
-css=span.action-group-footer > a.usa-button |
+css=.action-group-footer > .action-group-footer--container > a.usa-button |
|
@@ -535,12 +535,12 @@ Imported from: AT-AT CI - New Portfolio-->
waitForElementPresent |
-css=.accordion-table__items > .accordion-table__item:nth-of-type(1) > .accordion-table__item-content > .environment-list__item > .label.label--below |
+css=.accordion-table__items > .accordion-table__item:nth-of-type(1) > .accordion-table__item-content > .environment-list__item > .label |
|
assertElementPresent |
-css=.accordion-table__items > .accordion-table__item:nth-of-type(1) > .accordion-table__item-content > .environment-list__item > .label.label--below |
+css=.accordion-table__items > .accordion-table__item:nth-of-type(1) > .accordion-table__item-content > .environment-list__item > .label |
|
diff --git a/uitests/Revoke_Portfolio_Member_Invite.html b/uitests/Revoke_Portfolio_Member_Invite.html
index 947bf526..ec74eac0 100644
--- a/uitests/Revoke_Portfolio_Member_Invite.html
+++ b/uitests/Revoke_Portfolio_Member_Invite.html
@@ -246,7 +246,7 @@ Imported from: AT-AT CI - New Portfolio-->
assertText |
css=.empty-state > h3 |
-*You don't have any Applications yet* |
+*You don’t have any Applications yet* |
waitForPageToLoad |
@@ -308,12 +308,12 @@ Imported from: AT-AT CI - Portfolio Settings-->
Imported from: AT-AT CI - Portfolio Settings-->
waitForElementPresent |
-css=button.usa-button.usa-button-primary.usa-button-big |
+css=button.usa-button.usa-button-primary |
|
assertText |
-css=button.usa-button.usa-button-primary.usa-button-big |
+css=button.usa-button.usa-button-primary |
Save Changes |
diff --git a/uitests/TO_Index_(Landing)_Page_-_Empty_State.html b/uitests/TO_Index_(Landing)_Page_-_Empty_State.html
index bc0e7d5a..832e159e 100644
--- a/uitests/TO_Index_(Landing)_Page_-_Empty_State.html
+++ b/uitests/TO_Index_(Landing)_Page_-_Empty_State.html
@@ -165,7 +165,7 @@ Imported from: AT-AT CI - login-->
assertText |
css=.empty-state > h3 |
-*You don't have any Applications yet* |
+*You don’t have any Applications yet* |
waitForPageToLoad |
diff --git a/uitests/TO_Index_with_Draft_TO.html b/uitests/TO_Index_with_Draft_TO.html
index 6e34eb31..7f5b9de3 100644
--- a/uitests/TO_Index_with_Draft_TO.html
+++ b/uitests/TO_Index_with_Draft_TO.html
@@ -174,7 +174,7 @@ Imported from: AT-AT CI - New Portfolio-->
assertText |
css=.empty-state > h3 |
-*You don't have any Applications yet* |
+*You don’t have any Applications yet* |
waitForPageToLoad |
diff --git a/uitests/TO_Index_with_TO.html b/uitests/TO_Index_with_TO.html
index 8065848c..46bcfae1 100644
--- a/uitests/TO_Index_with_TO.html
+++ b/uitests/TO_Index_with_TO.html
@@ -174,7 +174,7 @@ Imported from: AT-AT CI - New Portfolio-->
assertText |
css=.empty-state > h3 |
-*You don't have any Applications yet* |
+*You don’t have any Applications yet* |
waitForPageToLoad |
diff --git a/uitests/TO_Index_with_Unsigned_TO.html b/uitests/TO_Index_with_Unsigned_TO.html
index 49998dbe..8093c99b 100644
--- a/uitests/TO_Index_with_Unsigned_TO.html
+++ b/uitests/TO_Index_with_Unsigned_TO.html
@@ -174,7 +174,7 @@ Imported from: AT-AT CI - New Portfolio-->
assertText |
css=.empty-state > h3 |
-*You don't have any Applications yet* |
+*You don’t have any Applications yet* |
waitForPageToLoad |
diff --git a/uitests/TO_Index_with_expired_TO.html b/uitests/TO_Index_with_expired_TO.html
index 47459209..d091166f 100644
--- a/uitests/TO_Index_with_expired_TO.html
+++ b/uitests/TO_Index_with_expired_TO.html
@@ -174,7 +174,7 @@ Imported from: AT-AT CI - New Portfolio-->
assertText |
css=.empty-state > h3 |
-*You don't have any Applications yet* |
+*You don’t have any Applications yet* |
waitForPageToLoad |
diff --git a/uitests/TO_Index_with_future_TO.html b/uitests/TO_Index_with_future_TO.html
index d04b0530..47d1d588 100644
--- a/uitests/TO_Index_with_future_TO.html
+++ b/uitests/TO_Index_with_future_TO.html
@@ -174,7 +174,7 @@ Imported from: AT-AT CI - New Portfolio-->
assertText |
css=.empty-state > h3 |
-*You don't have any Applications yet* |
+*You don’t have any Applications yet* |
waitForPageToLoad |
diff --git a/uitests/TO_Step_1.html b/uitests/TO_Step_1.html
index ac031608..aac33dd1 100644
--- a/uitests/TO_Step_1.html
+++ b/uitests/TO_Step_1.html
@@ -165,7 +165,7 @@ Imported from: AT-AT CI - login-->
assertText |
css=.empty-state > h3 |
-*You don't have any Applications yet* |
+*You don’t have any Applications yet* |
waitForPageToLoad |
diff --git a/uitests/TO_Step_2.html b/uitests/TO_Step_2.html
index 464d0f51..528b2b1a 100644
--- a/uitests/TO_Step_2.html
+++ b/uitests/TO_Step_2.html
@@ -174,7 +174,7 @@ Imported from: AT-AT CI - New Portfolio-->
assertText |
css=.empty-state > h3 |
-*You don't have any Applications yet* |
+*You don’t have any Applications yet* |
waitForPageToLoad |
diff --git a/uitests/TO_Step_3.html b/uitests/TO_Step_3.html
index ffdf0ae4..8f7e5ae9 100644
--- a/uitests/TO_Step_3.html
+++ b/uitests/TO_Step_3.html
@@ -183,7 +183,7 @@ Imported from: AT-AT CI - New Portfolio-->
assertText |
css=.empty-state > h3 |
-*You don't have any Applications yet* |
+*You don’t have any Applications yet* |
waitForPageToLoad |
diff --git a/uitests/TO_Step_3_-_Add_CLIN.html b/uitests/TO_Step_3_-_Add_CLIN.html
index 4617ebdd..859ab2c3 100644
--- a/uitests/TO_Step_3_-_Add_CLIN.html
+++ b/uitests/TO_Step_3_-_Add_CLIN.html
@@ -192,7 +192,7 @@ Imported from: AT-AT CI - New Portfolio-->
assertText |
css=.empty-state > h3 |
-*You don't have any Applications yet* |
+*You don’t have any Applications yet* |
waitForPageToLoad |
diff --git a/uitests/TO_Step_4.html b/uitests/TO_Step_4.html
index 8bf2f648..56087c82 100644
--- a/uitests/TO_Step_4.html
+++ b/uitests/TO_Step_4.html
@@ -192,7 +192,7 @@ Imported from: AT-AT CI - New Portfolio-->
assertText |
css=.empty-state > h3 |
-*You don't have any Applications yet* |
+*You don’t have any Applications yet* |
waitForPageToLoad |
diff --git a/uitests/TO_Step_5.html b/uitests/TO_Step_5.html
index 4eb53ab9..3c6f4db9 100644
--- a/uitests/TO_Step_5.html
+++ b/uitests/TO_Step_5.html
@@ -201,7 +201,7 @@ Imported from: AT-AT CI - New Portfolio-->
assertText |
css=.empty-state > h3 |
-*You don't have any Applications yet* |
+*You don’t have any Applications yet* |
waitForPageToLoad |