Skip to content

web: NPM Workspaces#14370

Open
GirlBossRush wants to merge 10 commits intomainfrom
workspace-lint-format
Open

web: NPM Workspaces#14370
GirlBossRush wants to merge 10 commits intomainfrom
workspace-lint-format

Conversation

@GirlBossRush
Copy link
Contributor

@GirlBossRush GirlBossRush commented May 3, 2025

Details

Depends on #14555

This PR updates our ESLint and Prettier scripts for use at the repo root. Also included is a collection of ignored "nightmare mode" rules that can be addressed in follow up PRs.

  • Fewer package-lock.json fixes, rebases, collisions
  • A fix for the generated API client de-synchronizing when installing dependencies
  • A single shared TypeScript context for checking types
    • Cached type checking across all packages while developing.
    • Faster type information when developing, and smaller type reconciliations when importing packages within the mono repo.

Checklist

  • Local tests pass (ak test authentik/)
  • The code has been formatted (make lint-fix)

If an API change has been made

  • The API schema has been updated (make gen-build)

If changes to the frontend have been made

  • The code has been formatted (make web)

If applicable

  • The documentation has been updated
  • The documentation has been formatted (make website)

@GirlBossRush GirlBossRush self-assigned this May 3, 2025
@GirlBossRush GirlBossRush requested review from a team as code owners May 3, 2025 02:26
@netlify
Copy link

netlify bot commented May 3, 2025

Deploy Preview for authentik-docs ready!

Name Link
🔨 Latest commit ab31550
🔍 Latest deploy log https://app.netlify.com/projects/authentik-docs/deploys/68305ad7e2083b00080226bb
😎 Deploy Preview https://deploy-preview-14370--authentik-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify
Copy link

netlify bot commented May 3, 2025

Deploy Preview for authentik-storybook ready!

Name Link
🔨 Latest commit ab31550
🔍 Latest deploy log https://app.netlify.com/projects/authentik-storybook/deploys/68305ad73f182700084aee5f
😎 Deploy Preview https://deploy-preview-14370--authentik-storybook.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@codecov
Copy link

codecov bot commented May 3, 2025

❌ 1 Tests Failed:

Tests completed Failed Passed Skipped
1806 1 1805 2
View the full list of 1 ❄️ flaky tests
tests.e2e.test_source_saml.TestSourceSAML::test_idp_post_auto_enroll_auth

Flake rate in main: 26.92% (Passed 19 times, Failed 7 times)

Stack Traces | 228s run time
self = <tests.e2e.test_source_saml.TestSourceSAML testMethod=test_idp_post_auto_enroll_auth>
args = (), kwargs = {}

    @wraps(func)
    def wrapper(self: TransactionTestCase, *args, **kwargs):
        """Run test again if we're below max_retries, including tearDown and
        setUp. Otherwise raise the error"""
        nonlocal count
        try:
>           return func(self, *args, **kwargs)

tests/e2e/utils.py:329: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_source_saml.TestSourceSAML testMethod=test_idp_post_auto_enroll_auth>,)
kwargs = {}, file = 'default/flow-default-invalidation-flow.yaml'
content = 'version: 1\nmetadata:\n  name: Default - Invalidation flow\nentries:\n- attrs:\n    designation: invalidation\n    na...0\n    stage: !KeyOf default-invalidation-logout\n    target: !KeyOf flow\n  model: authentik_flows.flowstagebinding\n'

    @wraps(func)
    def wrapper(*args, **kwargs):
        for file in files:
            content = BlueprintInstance(path=file).retrieve()
            Importer.from_string(content).apply()
>       return func(*args, **kwargs)

.../blueprints/tests/__init__.py:25: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_source_saml.TestSourceSAML testMethod=test_idp_post_auto_enroll_auth>,)
kwargs = {}, file = 'default/flow-default-source-pre-authentication.yaml'
content = 'version: 1\nmetadata:\n  name: Default - Source pre-authentication flow\nentries:\n- attrs:\n    designation: stage_c...    authentication: none\n  identifiers:\n    slug: default-source-pre-authentication\n  model: authentik_flows.flow\n'

    @wraps(func)
    def wrapper(*args, **kwargs):
        for file in files:
            content = BlueprintInstance(path=file).retrieve()
            Importer.from_string(content).apply()
>       return func(*args, **kwargs)

.../blueprints/tests/__init__.py:25: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_source_saml.TestSourceSAML testMethod=test_idp_post_auto_enroll_auth>

    @retry()
    @apply_blueprint(
        "default/flow-default-authentication-flow.yaml",
        "default/flow-default-invalidation-flow.yaml",
    )
    @apply_blueprint(
        "default/flow-default-source-authentication.yaml",
        "default/flow-default-source-enrollment.yaml",
        "default/flow-default-source-pre-authentication.yaml",
    )
    def test_idp_post_auto_enroll_auth(self):
        """test SAML Source With post binding (auto redirect)"""
        # Bootstrap all needed objects
        authentication_flow = Flow.objects.get(slug="default-source-authentication")
        enrollment_flow = Flow.objects.get(slug="default-source-enrollment")
        pre_authentication_flow = Flow.objects.get(slug="default-source-pre-authentication")
        keypair = CertificateKeyPair.objects.create(
            name=generate_id(),
            certificate_data=IDP_CERT,
            key_data=IDP_KEY,
        )
    
        source = SAMLSource.objects.create(
            name=generate_id(),
            slug=self.slug,
            authentication_flow=authentication_flow,
            enrollment_flow=enrollment_flow,
            pre_authentication_flow=pre_authentication_flow,
            issuer="entity-id",
            sso_url=f"http://{self.host}:.../saml2/idp/SSOService.php",
            binding_type=SAMLBindingTypes.POST_AUTO,
            signing_kp=keypair,
        )
        ident_stage = IdentificationStage.objects.first()
        ident_stage.sources.set([source])
        ident_stage.save()
    
        self.driver.get(self.live_server_url)
    
        flow_executor = self.get_shadow_root("ak-flow-executor")
        identification_stage = self.get_shadow_root("ak-stage-identification", flow_executor)
        wait = WebDriverWait(identification_stage, self.wait_timeout)
    
        wait.until(
            ec.presence_of_element_located(
                (By.CSS_SELECTOR, ".pf-c-login__main-footer-links-item > button")
            )
        )
        identification_stage.find_element(
            By.CSS_SELECTOR, ".pf-c-login__main-footer-links-item > button"
        ).click()
    
        # Now we should be at the IDP, wait for the username field
        self.wait.until(ec.presence_of_element_located((By.ID, "username")))
        self.driver.find_element(By.ID, "username").send_keys("user1")
        self.driver.find_element(By.ID, "password").send_keys("user1pass")
        self.driver.find_element(By.ID, "password").send_keys(Keys.ENTER)
    
        # Wait until we're logged in
        self.wait_for_url(self.if_user_url())
    
        self.assert_user(
            User.objects.exclude(username="akadmin")
            .exclude(username__startswith="ak-outpost")
            .exclude_anonymous()
            .exclude(pk=self.user.pk)
            .first()
        )
    
        # Clear all cookies and log in again
        self.driver.delete_all_cookies()
        self.driver.get(self.live_server_url)
    
        flow_executor = self.get_shadow_root("ak-flow-executor")
        identification_stage = self.get_shadow_root("ak-stage-identification", flow_executor)
        wait = WebDriverWait(identification_stage, self.wait_timeout)
    
        wait.until(
            ec.presence_of_element_located(
                (By.CSS_SELECTOR, ".pf-c-login__main-footer-links-item > button")
            )
        )
        identification_stage.find_element(
            By.CSS_SELECTOR, ".pf-c-login__main-footer-links-item > button"
        ).click()
    
        # Now we should be at the IDP, wait for the username field
>       self.wait.until(ec.presence_of_element_located((By.ID, "username")))

tests/e2e/test_source_saml.py:414: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <selenium.webdriver.support.wait.WebDriverWait (session="ff4f0b0b4efe5a91b46f64b725cd9e8f")>
method = <function presence_of_element_located.<locals>._predicate at 0x7fb4b7384c20>
message = ''

    def until(self, method: Callable[[D], Union[Literal[False], T]], message: str = "") -> T:
        """Wait until the method returns a value that is not False.
    
        Calls the method provided with the driver as an argument until the
        return value does not evaluate to ``False``.
    
        Parameters:
        -----------
        method: callable(WebDriver)
            - A callable object that takes a WebDriver instance as an argument.
    
        message: str
            - Optional message for :exc:`TimeoutException`
    
        Return:
        -------
        object: T
            - The result of the last call to `method`
    
        Raises:
        -------
        TimeoutException
            - If 'method' does not return a truthy value within the WebDriverWait
            object's timeout
    
        Example:
        --------
        >>> from selenium.webdriver.common.by import By
        >>> from selenium.webdriver.support.ui import WebDriverWait
        >>> from selenium.webdriver.support import expected_conditions as EC
    
        # Wait until an element is visible on the page
        >>> wait = WebDriverWait(driver, 10)
        >>> element = wait.until(EC.visibility_of_element_located((By.ID, "exampleId")))
        >>> print(element.text)
        """
        screen = None
        stacktrace = None
    
        end_time = time.monotonic() + self._timeout
        while True:
            try:
                value = method(self._driver)
                if value:
                    return value
            except self._ignored_exceptions as exc:
                screen = getattr(exc, "screen", None)
                stacktrace = getattr(exc, "stacktrace", None)
            if time.monotonic() > end_time:
                break
            time.sleep(self._poll)
>       raise TimeoutException(message, screen, stacktrace)
E       selenium.common.exceptions.TimeoutException: Message: 
E       Stacktrace:
E       #0 0x557ba43f975a <unknown>
E       #1 0x557ba3e9c0a0 <unknown>
E       #2 0x557ba3eed9b0 <unknown>
E       #3 0x557ba3eedba1 <unknown>
E       #4 0x557ba3f3bea4 <unknown>
E       #5 0x557ba3f133cd <unknown>
E       #6 0x557ba3f392a0 <unknown>
E       #7 0x557ba3f13173 <unknown>
E       #8 0x557ba3edfd4b <unknown>
E       #9 0x557ba3ee09b1 <unknown>
E       #10 0x557ba43be90b <unknown>
E       #11 0x557ba43c280a <unknown>
E       #12 0x557ba43a6662 <unknown>
E       #13 0x557ba43c3394 <unknown>
E       #14 0x557ba438b49f <unknown>
E       #15 0x557ba43e7538 <unknown>
E       #16 0x557ba43e7716 <unknown>
E       #17 0x557ba43f85c6 <unknown>
E       #18 0x7f166ab35aa4 <unknown>
E       #19 0x7f166abc2a34 __clone

.venv/lib/python3.13.../webdriver/support/wait.py:146: TimeoutException

During handling of the above exception, another exception occurred:

self = <tests.e2e.test_source_saml.TestSourceSAML testMethod=test_idp_post_auto_enroll_auth>
args = (), kwargs = {}

    @wraps(func)
    def wrapper(self: TransactionTestCase, *args, **kwargs):
        """Run test again if we're below max_retries, including tearDown and
        setUp. Otherwise raise the error"""
        nonlocal count
        try:
>           return func(self, *args, **kwargs)

tests/e2e/utils.py:329: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_source_saml.TestSourceSAML testMethod=test_idp_post_auto_enroll_auth>,)
kwargs = {}, file = 'default/flow-default-invalidation-flow.yaml'
content = 'version: 1\nmetadata:\n  name: Default - Invalidation flow\nentries:\n- attrs:\n    designation: invalidation\n    na...0\n    stage: !KeyOf default-invalidation-logout\n    target: !KeyOf flow\n  model: authentik_flows.flowstagebinding\n'

    @wraps(func)
    def wrapper(*args, **kwargs):
        for file in files:
            content = BlueprintInstance(path=file).retrieve()
            Importer.from_string(content).apply()
>       return func(*args, **kwargs)

.../blueprints/tests/__init__.py:25: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_source_saml.TestSourceSAML testMethod=test_idp_post_auto_enroll_auth>,)
kwargs = {}, file = 'default/flow-default-source-pre-authentication.yaml'
content = 'version: 1\nmetadata:\n  name: Default - Source pre-authentication flow\nentries:\n- attrs:\n    designation: stage_c...    authentication: none\n  identifiers:\n    slug: default-source-pre-authentication\n  model: authentik_flows.flow\n'

    @wraps(func)
    def wrapper(*args, **kwargs):
        for file in files:
            content = BlueprintInstance(path=file).retrieve()
            Importer.from_string(content).apply()
>       return func(*args, **kwargs)

.../blueprints/tests/__init__.py:25: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_source_saml.TestSourceSAML testMethod=test_idp_post_auto_enroll_auth>

    @retry()
    @apply_blueprint(
        "default/flow-default-authentication-flow.yaml",
        "default/flow-default-invalidation-flow.yaml",
    )
    @apply_blueprint(
        "default/flow-default-source-authentication.yaml",
        "default/flow-default-source-enrollment.yaml",
        "default/flow-default-source-pre-authentication.yaml",
    )
    def test_idp_post_auto_enroll_auth(self):
        """test SAML Source With post binding (auto redirect)"""
        # Bootstrap all needed objects
        authentication_flow = Flow.objects.get(slug="default-source-authentication")
        enrollment_flow = Flow.objects.get(slug="default-source-enrollment")
        pre_authentication_flow = Flow.objects.get(slug="default-source-pre-authentication")
        keypair = CertificateKeyPair.objects.create(
            name=generate_id(),
            certificate_data=IDP_CERT,
            key_data=IDP_KEY,
        )
    
        source = SAMLSource.objects.create(
            name=generate_id(),
            slug=self.slug,
            authentication_flow=authentication_flow,
            enrollment_flow=enrollment_flow,
            pre_authentication_flow=pre_authentication_flow,
            issuer="entity-id",
            sso_url=f"http://{self.host}:.../saml2/idp/SSOService.php",
            binding_type=SAMLBindingTypes.POST_AUTO,
            signing_kp=keypair,
        )
        ident_stage = IdentificationStage.objects.first()
        ident_stage.sources.set([source])
        ident_stage.save()
    
        self.driver.get(self.live_server_url)
    
        flow_executor = self.get_shadow_root("ak-flow-executor")
        identification_stage = self.get_shadow_root("ak-stage-identification", flow_executor)
        wait = WebDriverWait(identification_stage, self.wait_timeout)
    
        wait.until(
            ec.presence_of_element_located(
                (By.CSS_SELECTOR, ".pf-c-login__main-footer-links-item > button")
            )
        )
        identification_stage.find_element(
            By.CSS_SELECTOR, ".pf-c-login__main-footer-links-item > button"
        ).click()
    
        # Now we should be at the IDP, wait for the username field
        self.wait.until(ec.presence_of_element_located((By.ID, "username")))
        self.driver.find_element(By.ID, "username").send_keys("user1")
        self.driver.find_element(By.ID, "password").send_keys("user1pass")
        self.driver.find_element(By.ID, "password").send_keys(Keys.ENTER)
    
        # Wait until we're logged in
        self.wait_for_url(self.if_user_url())
    
        self.assert_user(
            User.objects.exclude(username="akadmin")
            .exclude(username__startswith="ak-outpost")
            .exclude_anonymous()
            .exclude(pk=self.user.pk)
            .first()
        )
    
        # Clear all cookies and log in again
        self.driver.delete_all_cookies()
        self.driver.get(self.live_server_url)
    
        flow_executor = self.get_shadow_root("ak-flow-executor")
        identification_stage = self.get_shadow_root("ak-stage-identification", flow_executor)
        wait = WebDriverWait(identification_stage, self.wait_timeout)
    
        wait.until(
            ec.presence_of_element_located(
                (By.CSS_SELECTOR, ".pf-c-login__main-footer-links-item > button")
            )
        )
        identification_stage.find_element(
            By.CSS_SELECTOR, ".pf-c-login__main-footer-links-item > button"
        ).click()
    
        # Now we should be at the IDP, wait for the username field
>       self.wait.until(ec.presence_of_element_located((By.ID, "username")))

tests/e2e/test_source_saml.py:414: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <selenium.webdriver.support.wait.WebDriverWait (session="aee5832839f7d5a2dbc30adce4cbd2b6")>
method = <function presence_of_element_located.<locals>._predicate at 0x7fb4b6ff0400>
message = ''

    def until(self, method: Callable[[D], Union[Literal[False], T]], message: str = "") -> T:
        """Wait until the method returns a value that is not False.
    
        Calls the method provided with the driver as an argument until the
        return value does not evaluate to ``False``.
    
        Parameters:
        -----------
        method: callable(WebDriver)
            - A callable object that takes a WebDriver instance as an argument.
    
        message: str
            - Optional message for :exc:`TimeoutException`
    
        Return:
        -------
        object: T
            - The result of the last call to `method`
    
        Raises:
        -------
        TimeoutException
            - If 'method' does not return a truthy value within the WebDriverWait
            object's timeout
    
        Example:
        --------
        >>> from selenium.webdriver.common.by import By
        >>> from selenium.webdriver.support.ui import WebDriverWait
        >>> from selenium.webdriver.support import expected_conditions as EC
    
        # Wait until an element is visible on the page
        >>> wait = WebDriverWait(driver, 10)
        >>> element = wait.until(EC.visibility_of_element_located((By.ID, "exampleId")))
        >>> print(element.text)
        """
        screen = None
        stacktrace = None
    
        end_time = time.monotonic() + self._timeout
        while True:
            try:
                value = method(self._driver)
                if value:
                    return value
            except self._ignored_exceptions as exc:
                screen = getattr(exc, "screen", None)
                stacktrace = getattr(exc, "stacktrace", None)
            if time.monotonic() > end_time:
                break
            time.sleep(self._poll)
>       raise TimeoutException(message, screen, stacktrace)
E       selenium.common.exceptions.TimeoutException: Message: 
E       Stacktrace:
E       #0 0x55910918575a <unknown>
E       #1 0x559108c280a0 <unknown>
E       #2 0x559108c799b0 <unknown>
E       #3 0x559108c79ba1 <unknown>
E       #4 0x559108cc7ea4 <unknown>
E       #5 0x559108c9f3cd <unknown>
E       #6 0x559108cc52a0 <unknown>
E       #7 0x559108c9f173 <unknown>
E       #8 0x559108c6bd4b <unknown>
E       #9 0x559108c6c9b1 <unknown>
E       #10 0x55910914a90b <unknown>
E       #11 0x55910914e80a <unknown>
E       #12 0x559109132662 <unknown>
E       #13 0x55910914f394 <unknown>
E       #14 0x55910911749f <unknown>
E       #15 0x559109173538 <unknown>
E       #16 0x559109173716 <unknown>
E       #17 0x5591091845c6 <unknown>
E       #18 0x7ff7c9382aa4 <unknown>
E       #19 0x7ff7c940fa34 __clone

.venv/lib/python3.13.../webdriver/support/wait.py:146: TimeoutException

During handling of the above exception, another exception occurred:

self = <unittest.case._Outcome object at 0x7fb4b7fe2580>
test_case = <tests.e2e.test_source_saml.TestSourceSAML testMethod=test_idp_post_auto_enroll_auth>
subTest = False

    @contextlib.contextmanager
    def testPartExecutor(self, test_case, subTest=False):
        old_success = self.success
        self.success = True
        try:
>           yield

.../hostedtoolcache/Python/3.13.3........./x64/lib/python3.13/unittest/case.py:58: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_source_saml.TestSourceSAML testMethod=test_idp_post_auto_enroll_auth>
result = <TestCaseFunction test_idp_post_auto_enroll_auth>

    def run(self, result=None):
        if result is None:
            result = self.defaultTestResult()
            startTestRun = getattr(result, 'startTestRun', None)
            stopTestRun = getattr(result, 'stopTestRun', None)
            if startTestRun is not None:
                startTestRun()
        else:
            stopTestRun = None
    
        result.startTest(self)
        try:
            testMethod = getattr(self, self._testMethodName)
            if (getattr(self.__class__, "__unittest_skip__", False) or
                getattr(testMethod, "__unittest_skip__", False)):
                # If the class or method was skipped.
                skip_why = (getattr(self.__class__, '__unittest_skip_why__', '')
                            or getattr(testMethod, '__unittest_skip_why__', ''))
                _addSkip(result, self, skip_why)
                return result
    
            expecting_failure = (
                getattr(self, "__unittest_expecting_failure__", False) or
                getattr(testMethod, "__unittest_expecting_failure__", False)
            )
            outcome = _Outcome(result)
            start_time = time.perf_counter()
            try:
                self._outcome = outcome
    
                with outcome.testPartExecutor(self):
                    self._callSetUp()
                if outcome.success:
                    outcome.expecting_failure = expecting_failure
                    with outcome.testPartExecutor(self):
>                       self._callTestMethod(testMethod)

.../hostedtoolcache/Python/3.13.3........./x64/lib/python3.13/unittest/case.py:651: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_source_saml.TestSourceSAML testMethod=test_idp_post_auto_enroll_auth>
method = <bound method TestSourceSAML.test_idp_post_auto_enroll_auth of <tests.e2e.test_source_saml.TestSourceSAML testMethod=test_idp_post_auto_enroll_auth>>

    def _callTestMethod(self, method):
>       if method() is not None:

.../hostedtoolcache/Python/3.13.3........./x64/lib/python3.13/unittest/case.py:606: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_source_saml.TestSourceSAML testMethod=test_idp_post_auto_enroll_auth>
args = (), kwargs = {}

    @wraps(func)
    def wrapper(self: TransactionTestCase, *args, **kwargs):
        """Run test again if we're below max_retries, including tearDown and
        setUp. Otherwise raise the error"""
        nonlocal count
        try:
            return func(self, *args, **kwargs)
    
        except tuple(exceptions) as exc:
            count += 1
            if count > max_retires:
                logger.debug("Exceeded retry count", exc=exc, test=self)
    
                raise exc
            logger.debug("Retrying on error", exc=exc, test=self)
            self.tearDown()
            self._post_teardown()
            self._pre_setup()
            self.setUp()
>           return wrapper(self, *args, **kwargs)

tests/e2e/utils.py:342: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_source_saml.TestSourceSAML testMethod=test_idp_post_auto_enroll_auth>
args = (), kwargs = {}

    @wraps(func)
    def wrapper(self: TransactionTestCase, *args, **kwargs):
        """Run test again if we're below max_retries, including tearDown and
        setUp. Otherwise raise the error"""
        nonlocal count
        try:
            return func(self, *args, **kwargs)
    
        except tuple(exceptions) as exc:
            count += 1
            if count > max_retires:
                logger.debug("Exceeded retry count", exc=exc, test=self)
    
                raise exc
            logger.debug("Retrying on error", exc=exc, test=self)
            self.tearDown()
            self._post_teardown()
            self._pre_setup()
            self.setUp()
>           return wrapper(self, *args, **kwargs)

tests/e2e/utils.py:342: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_source_saml.TestSourceSAML testMethod=test_idp_post_auto_enroll_auth>
args = (), kwargs = {}

    @wraps(func)
    def wrapper(self: TransactionTestCase, *args, **kwargs):
        """Run test again if we're below max_retries, including tearDown and
        setUp. Otherwise raise the error"""
        nonlocal count
        try:
            return func(self, *args, **kwargs)
    
        except tuple(exceptions) as exc:
            count += 1
            if count > max_retires:
                logger.debug("Exceeded retry count", exc=exc, test=self)
    
>               raise exc

tests/e2e/utils.py:336: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_source_saml.TestSourceSAML testMethod=test_idp_post_auto_enroll_auth>
args = (), kwargs = {}

    @wraps(func)
    def wrapper(self: TransactionTestCase, *args, **kwargs):
        """Run test again if we're below max_retries, including tearDown and
        setUp. Otherwise raise the error"""
        nonlocal count
        try:
>           return func(self, *args, **kwargs)

tests/e2e/utils.py:329: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_source_saml.TestSourceSAML testMethod=test_idp_post_auto_enroll_auth>,)
kwargs = {}, file = 'default/flow-default-invalidation-flow.yaml'
content = 'version: 1\nmetadata:\n  name: Default - Invalidation flow\nentries:\n- attrs:\n    designation: invalidation\n    na...0\n    stage: !KeyOf default-invalidation-logout\n    target: !KeyOf flow\n  model: authentik_flows.flowstagebinding\n'

    @wraps(func)
    def wrapper(*args, **kwargs):
        for file in files:
            content = BlueprintInstance(path=file).retrieve()
            Importer.from_string(content).apply()
>       return func(*args, **kwargs)

.../blueprints/tests/__init__.py:25: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_source_saml.TestSourceSAML testMethod=test_idp_post_auto_enroll_auth>,)
kwargs = {}, file = 'default/flow-default-source-pre-authentication.yaml'
content = 'version: 1\nmetadata:\n  name: Default - Source pre-authentication flow\nentries:\n- attrs:\n    designation: stage_c...    authentication: none\n  identifiers:\n    slug: default-source-pre-authentication\n  model: authentik_flows.flow\n'

    @wraps(func)
    def wrapper(*args, **kwargs):
        for file in files:
            content = BlueprintInstance(path=file).retrieve()
            Importer.from_string(content).apply()
>       return func(*args, **kwargs)

.../blueprints/tests/__init__.py:25: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_source_saml.TestSourceSAML testMethod=test_idp_post_auto_enroll_auth>

    @retry()
    @apply_blueprint(
        "default/flow-default-authentication-flow.yaml",
        "default/flow-default-invalidation-flow.yaml",
    )
    @apply_blueprint(
        "default/flow-default-source-authentication.yaml",
        "default/flow-default-source-enrollment.yaml",
        "default/flow-default-source-pre-authentication.yaml",
    )
    def test_idp_post_auto_enroll_auth(self):
        """test SAML Source With post binding (auto redirect)"""
        # Bootstrap all needed objects
        authentication_flow = Flow.objects.get(slug="default-source-authentication")
        enrollment_flow = Flow.objects.get(slug="default-source-enrollment")
        pre_authentication_flow = Flow.objects.get(slug="default-source-pre-authentication")
        keypair = CertificateKeyPair.objects.create(
            name=generate_id(),
            certificate_data=IDP_CERT,
            key_data=IDP_KEY,
        )
    
        source = SAMLSource.objects.create(
            name=generate_id(),
            slug=self.slug,
            authentication_flow=authentication_flow,
            enrollment_flow=enrollment_flow,
            pre_authentication_flow=pre_authentication_flow,
            issuer="entity-id",
            sso_url=f"http://{self.host}:.../saml2/idp/SSOService.php",
            binding_type=SAMLBindingTypes.POST_AUTO,
            signing_kp=keypair,
        )
        ident_stage = IdentificationStage.objects.first()
        ident_stage.sources.set([source])
        ident_stage.save()
    
        self.driver.get(self.live_server_url)
    
        flow_executor = self.get_shadow_root("ak-flow-executor")
        identification_stage = self.get_shadow_root("ak-stage-identification", flow_executor)
        wait = WebDriverWait(identification_stage, self.wait_timeout)
    
        wait.until(
            ec.presence_of_element_located(
                (By.CSS_SELECTOR, ".pf-c-login__main-footer-links-item > button")
            )
        )
        identification_stage.find_element(
            By.CSS_SELECTOR, ".pf-c-login__main-footer-links-item > button"
        ).click()
    
        # Now we should be at the IDP, wait for the username field
        self.wait.until(ec.presence_of_element_located((By.ID, "username")))
        self.driver.find_element(By.ID, "username").send_keys("user1")
        self.driver.find_element(By.ID, "password").send_keys("user1pass")
        self.driver.find_element(By.ID, "password").send_keys(Keys.ENTER)
    
        # Wait until we're logged in
        self.wait_for_url(self.if_user_url())
    
        self.assert_user(
            User.objects.exclude(username="akadmin")
            .exclude(username__startswith="ak-outpost")
            .exclude_anonymous()
            .exclude(pk=self.user.pk)
            .first()
        )
    
        # Clear all cookies and log in again
        self.driver.delete_all_cookies()
        self.driver.get(self.live_server_url)
    
        flow_executor = self.get_shadow_root("ak-flow-executor")
        identification_stage = self.get_shadow_root("ak-stage-identification", flow_executor)
        wait = WebDriverWait(identification_stage, self.wait_timeout)
    
        wait.until(
            ec.presence_of_element_located(
                (By.CSS_SELECTOR, ".pf-c-login__main-footer-links-item > button")
            )
        )
        identification_stage.find_element(
            By.CSS_SELECTOR, ".pf-c-login__main-footer-links-item > button"
        ).click()
    
        # Now we should be at the IDP, wait for the username field
>       self.wait.until(ec.presence_of_element_located((By.ID, "username")))

tests/e2e/test_source_saml.py:414: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <selenium.webdriver.support.wait.WebDriverWait (session="2b12d7244dfedaacc77fee57637a5726")>
method = <function presence_of_element_located.<locals>._predicate at 0x7fb4b741f560>
message = ''

    def until(self, method: Callable[[D], Union[Literal[False], T]], message: str = "") -> T:
        """Wait until the method returns a value that is not False.
    
        Calls the method provided with the driver as an argument until the
        return value does not evaluate to ``False``.
    
        Parameters:
        -----------
        method: callable(WebDriver)
            - A callable object that takes a WebDriver instance as an argument.
    
        message: str
            - Optional message for :exc:`TimeoutException`
    
        Return:
        -------
        object: T
            - The result of the last call to `method`
    
        Raises:
        -------
        TimeoutException
            - If 'method' does not return a truthy value within the WebDriverWait
            object's timeout
    
        Example:
        --------
        >>> from selenium.webdriver.common.by import By
        >>> from selenium.webdriver.support.ui import WebDriverWait
        >>> from selenium.webdriver.support import expected_conditions as EC
    
        # Wait until an element is visible on the page
        >>> wait = WebDriverWait(driver, 10)
        >>> element = wait.until(EC.visibility_of_element_located((By.ID, "exampleId")))
        >>> print(element.text)
        """
        screen = None
        stacktrace = None
    
        end_time = time.monotonic() + self._timeout
        while True:
            try:
                value = method(self._driver)
                if value:
                    return value
            except self._ignored_exceptions as exc:
                screen = getattr(exc, "screen", None)
                stacktrace = getattr(exc, "stacktrace", None)
            if time.monotonic() > end_time:
                break
            time.sleep(self._poll)
>       raise TimeoutException(message, screen, stacktrace)
E       selenium.common.exceptions.TimeoutException: Message: 
E       Stacktrace:
E       #0 0x5642f401475a <unknown>
E       #1 0x5642f3ab70a0 <unknown>
E       #2 0x5642f3b089b0 <unknown>
E       #3 0x5642f3b08ba1 <unknown>
E       #4 0x5642f3b56ea4 <unknown>
E       #5 0x5642f3b2e3cd <unknown>
E       #6 0x5642f3b542a0 <unknown>
E       #7 0x5642f3b2e173 <unknown>
E       #8 0x5642f3afad4b <unknown>
E       #9 0x5642f3afb9b1 <unknown>
E       #10 0x5642f3fd990b <unknown>
E       #11 0x5642f3fdd80a <unknown>
E       #12 0x5642f3fc1662 <unknown>
E       #13 0x5642f3fde394 <unknown>
E       #14 0x5642f3fa649f <unknown>
E       #15 0x5642f4002538 <unknown>
E       #16 0x5642f4002716 <unknown>
E       #17 0x5642f40135c6 <unknown>
E       #18 0x7fc573207aa4 <unknown>
E       #19 0x7fc573294a34 __clone

.venv/lib/python3.13.../webdriver/support/wait.py:146: TimeoutException

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

@GirlBossRush GirlBossRush force-pushed the workspace-lint-format branch from 3fe56fc to 66509d2 Compare May 3, 2025 11:55
Copy link
Collaborator

@gergosimonyi gergosimonyi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Backend (which here is nothing) ✅

@GirlBossRush GirlBossRush force-pushed the workspace-lint-format branch from 66509d2 to 5e75212 Compare May 13, 2025 03:57
@GirlBossRush GirlBossRush force-pushed the workspace-lint-format branch from 5e75212 to 3b80770 Compare May 14, 2025 13:30
@GirlBossRush GirlBossRush requested a review from a team as a code owner May 14, 2025 14:05
@GirlBossRush GirlBossRush force-pushed the workspace-lint-format branch 2 times, most recently from 4d68592 to 7bba5cf Compare May 14, 2025 14:49
@github-actions
Copy link
Contributor

github-actions bot commented May 14, 2025

authentik PR Installation instructions

Instructions for docker-compose

Add the following block to your .env file:

AUTHENTIK_IMAGE=ghcr.io/goauthentik/dev-server
AUTHENTIK_TAG=gh-10e854486f33a9de2f509f3b5f6e4dbf6a519581
AUTHENTIK_OUTPOSTS__CONTAINER_IMAGE_BASE=ghcr.io/goauthentik/dev-%(type)s:gh-%(build_hash)s

Afterwards, run the upgrade commands from the latest release notes.

Instructions for Kubernetes

Add the following block to your values.yml file:

authentik:
    outposts:
        container_image_base: ghcr.io/goauthentik/dev-%(type)s:gh-%(build_hash)s
global:
    image:
        repository: ghcr.io/goauthentik/dev-server
        tag: gh-10e854486f33a9de2f509f3b5f6e4dbf6a519581

Afterwards, run the upgrade commands from the latest release notes.

@GirlBossRush GirlBossRush force-pushed the workspace-lint-format branch 11 times, most recently from c76f52d to 5dd1662 Compare May 15, 2025 16:33
@GirlBossRush GirlBossRush requested a review from a team as a code owner May 15, 2025 17:13
@GirlBossRush GirlBossRush force-pushed the workspace-lint-format branch 3 times, most recently from b61c57d to dc12ab4 Compare May 16, 2025 14:54
@GirlBossRush GirlBossRush force-pushed the workspace-lint-format branch 5 times, most recently from 022f958 to 10e8544 Compare May 17, 2025 03:18
@GirlBossRush GirlBossRush changed the title web: Prep NPM Workspace linter/formatter. web: NPM Workspaces May 17, 2025
@GirlBossRush GirlBossRush force-pushed the workspace-lint-format branch from faec346 to 69071eb Compare May 21, 2025 22:16
@GirlBossRush GirlBossRush requested a review from BeryJu May 21, 2025 22:20
@GirlBossRush GirlBossRush force-pushed the workspace-lint-format branch from 69071eb to 96a4a5f Compare May 21, 2025 23:01
@GirlBossRush GirlBossRush force-pushed the workspace-lint-format branch from 96a4a5f to ab31550 Compare May 23, 2025 11:24
ARG GIT_BUILD_HASH
ENV GIT_BUILD_HASH=$GIT_BUILD_HASH

RUN --mount=type=bind,target=/work/website/package.json,src=./website/package.json \
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the goal here is to only re-run this step when package.json or package-lock.json changed, and thus improve build speeds when only the code changes. I don't know how that would work with workspaces, probably need to mount a few more files, but in any case, we should preserve the current behavior

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've only added this comment here, but it also goes for other changed dockerfiles

Comment on lines +35 to +41
- uses: actions/setup-node@v4
with:
node-version-file: package.json
cache: "npm"
cache-dependency-path: package-lock.json
- name: Prepare Dependencies
run: npm ci
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this already done by .github/actions/setup?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants