Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve i18n Test Output and Add Detailed Logging #2876

Open
wants to merge 8 commits into
base: master
Choose a base branch
from

Conversation

rsoaresdev
Copy link
Contributor

@rsoaresdev rsoaresdev commented Dec 27, 2024

Improve i18n Test Output and Add Detailed Logging

Please verify the following:

  • yarn test jest tests pass with new tests, if relevant
  • yarn lint eslint checks pass with new code, if relevant
  • yarn format:check prettier checks pass with new code, if relevant
  • README.md (or relevant documentation) has been updated with your changes
  • If this affects functionality there aren't tests for, I manually tested it, including by generating a new app locally if needed (see docs).

Describe your PR

Description:

•	Enhanced the i18n test to provide better output formatting in the terminal.
•	Added a clean and well-structured block output that includes details about total keys defined, total keys used, missing keys, and unused keys.
•	Implemented recursive collection of translation keys using collectKeys() for a more accurate comparison between defined and used keys.
•	The output now appears in a block with clear separation, making it easier to read and debug the results.
•	Adjusted the grep command to handle i18n key extraction more effectively.

Changes:

•	Refactored the i18n test to improve clarity and structure.
•	Introduced a new output format for better readability during test runs.
•	Added detailed comments to explain the logic behind each step in the test.
•	Enhanced error handling for retries during the grep command execution.

This update improves the overall test process for missing or unused translation keys, making it easier to catch issues and understand the results.

Screenshots (if applicable)

Captura de ecrã 2024-12-27, às 00 28 44
Captura de ecrã 2024-12-27, às 00 29 23

Improve i18n Test Output and Add Detailed Logging
@rsoaresdev rsoaresdev changed the title Update i18n.test.ts Improve i18n Test Output and Add Detailed Logging Dec 27, 2024
rsoaresdev and others added 2 commits December 27, 2024 00:39
Fix grep command to work with absolute paths in CircleCI
Copy link
Contributor

@mazenchami mazenchami left a comment

Choose a reason for hiding this comment

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

@rsoaresdev thank you for putting this together. this is a great addition!

let output = `--- i18n Test Results ---\n`
output += `Total keys defined: ${allKeysDefined.length}\n`
output += `Total keys used: ${allKeysUsed.length}\n`
output += `Missing keys: ${missingKeys.length > 0 ? missingKeys.join(", ") : "None"}\n`
Copy link
Collaborator

Choose a reason for hiding this comment

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

💯
this is nice

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks, glad you liked it. That output is actually very useful.

@flexbox
Copy link
Collaborator

flexbox commented Jan 18, 2025

I don't know why circle ci is not happy but locally I have this error when I run yarn test (v4.6.0).

CleanShot 2025-01-18 at 23 04 52@2x

      ● i18n › There are no missing or unused keys

        Test failed: Command failed after multiple attempts: Command failed: grep -E '[Tt]x=[{]?"[^"]*"[}]?|translate\("[^"]*"' -ohr /private/var/folders/ph/3dx8n1nn70x4ppk5x7ns464c0000gn/T/ignite-86ed4c3d7bb64dcebe2d09cc13ba116e/Foo/app | grep -o '"[^"]*"'
        grep: /private/var/folders/ph/3dx8n1nn70x4ppk5x7ns464c0000gn/T/ignite-86ed4c3d7bb64dcebe2d09cc13ba116e/Foo/app: No such file or directory

          111 |     } catch (error) {
          112 |       // If any error occurs, throw an exception to fail the test
        > 113 |       throw new Error(`Test failed: ${error.message}`)
              |             ^
          114 |     }
          115 |   }, 240000) // 4 minutes timeout
          116 | })

@rsoaresdev
Copy link
Contributor Author

I don't know why circle ci is not happy but locally I have this error when I run yarn test (v4.6.0).

CleanShot 2025-01-18 at 23 04 52@2x

      ● i18n › There are no missing or unused keys

        Test failed: Command failed after multiple attempts: Command failed: grep -E '[Tt]x=[{]?"[^"]*"[}]?|translate\("[^"]*"' -ohr /private/var/folders/ph/3dx8n1nn70x4ppk5x7ns464c0000gn/T/ignite-86ed4c3d7bb64dcebe2d09cc13ba116e/Foo/app | grep -o '"[^"]*"'
        grep: /private/var/folders/ph/3dx8n1nn70x4ppk5x7ns464c0000gn/T/ignite-86ed4c3d7bb64dcebe2d09cc13ba116e/Foo/app: No such file or directory

          111 |     } catch (error) {
          112 |       // If any error occurs, throw an exception to fail the test
        > 113 |       throw new Error(`Test failed: ${error.message}`)
              |             ^
          114 |     }
          115 |   }, 240000) // 4 minutes timeout
          116 | })

The test uses process.cwd() to define the srcDir (process.cwd()/app), but Yarn’s sandbox creates temporary paths like /private/var/folders/..., which don’t always contain the expected project structure. When the grep command tries to scan these directories, it fails because the srcDir doesn’t exist.

Can you try this way:

// other imports
import path from "path"

// ...
const CONFIG = {
  srcDir: path.join(__dirname, "..", "app"),
  // ...
}

- Add robust directory detection for various environments (Yarn, Bun, npm)
- Handle temporary directories
- Add fallback paths and better error messages
- Verify directory existence before running grep command
@rsoaresdev
Copy link
Contributor Author

With my last commit the issue finding the right path is fixed. Now circle ci is not happy because it encounters many keys that are missing:

    FAIL test/i18n.test.ts
      ● Console

        console.log
          --- i18n Test Results ---
          Total keys defined: 12
          Total keys used: 230
          Missing keys: welcomeScreen:letsGo, loginScreen:logIn, loginScreen:enterDetails, loginScreen:hint, loginScreen:emailFieldLabel, loginScreen:emailFieldPlaceholder, loginScreen:passwordFieldLabel, loginScreen:passwordFieldPlaceholder, loginScreen:tapToLogIn, demoShowroomScreen:jumpStart, demoAutoImage:useCase.scaledToFitDimensions.heightAuto, demoAutoImage:useCase.scaledToFitDimensions.widthAuto, demoAutoImage:useCase.scaledToFitDimensions.bothManual, demoButton:useCase.passingContent.viaTextProps, demoShowroomScreen:demoViaTxProp, demoButton:useCase.passingContent.children, demoButton:useCase.passingContent.rightAccessory, demoButton:useCase.passingContent.leftAccessory, demoButton:useCase.passingContent.nestedChildren, demoButton:useCase.passingContent.nestedChildren2, demoButton:useCase.passingContent.nestedChildren3, demoButton:useCase.passingContent.multiLine, demoButton:useCase.styling.styleContainer, demoButton:useCase.styling.styleText, demoButton:useCase.styling.styleAccessories, demoButton:useCase.styling.pressedState, demoButton:useCase.disabling.standard, demoButton:useCase.disabling.filled, demoButton:useCase.disabling.reversed, demoButton:useCase.disabling.accessory, demoButton:useCase.disabling.textStyle, demoCard:useCase.presets.default.heading, demoCard:useCase.presets.default.content, demoCard:useCase.presets.default.footer, demoCard:useCase.presets.reversed.heading, demoCard:useCase.presets.reversed.content, demoCard:useCase.presets.reversed.footer, demoCard:useCase.verticalAlignment.top.heading, demoCard:useCase.verticalAlignment.top.content, demoCard:useCase.verticalAlignment.top.footer, demoCard:useCase.verticalAlignment.center.heading, demoCard:useCase.verticalAlignment.center.content, demoCard:useCase.verticalAlignment.center.footer, demoCard:useCase.verticalAlignment.spaceBetween.heading, demoCard:useCase.verticalAlignment.spaceBetween.content, demoCard:useCase.verticalAlignment.spaceBetween.footer, demoCard:useCase.verticalAlignment.reversed.heading, demoCard:useCase.verticalAlignment.reversed.content, demoCard:useCase.verticalAlignment.reversed.footer, demoCard:useCase.passingContent.heading, demoCard:useCase.passingContent.content, demoCard:useCase.passingContent.footer, demoShowroomScreen:demoViaSpecifiedTxProp, demoShowroomScreen:demoViaSpecifiedTxProp, demoShowroomScreen:demoViaSpecifiedTxProp, demoCard:useCase.customComponent.rightComponent, demoCard:useCase.customComponent.leftComponent, demoCard:useCase.style.heading, demoCard:useCase.style.content, demoCard:useCase.style.footer, demoEmptyState:useCase.passingContent.customizeImageHeading, demoEmptyState:useCase.passingContent.customizeImageContent, demoEmptyState:useCase.passingContent.viaHeadingProp, demoEmptyState:useCase.passingContent.viaContentProp, demoEmptyState:useCase.passingContent.viaButtonProp, demoShowroomScreen:demoViaSpecifiedTxProp, demoShowroomScreen:demoViaSpecifiedTxProp, demoShowroomScreen:demoViaSpecifiedTxProp, demoToggle:useCase.variants.checkbox.label, demoToggle:useCase.variants.checkbox.helper, demoToggle:useCase.variants.radio.label, demoToggle:useCase.variants.radio.helper, demoToggle:useCase.variants.switch.label, demoToggle:useCase.variants.switch.helper, demoToggle:useCase.statuses.noStatus, demoToggle:useCase.statuses.errorStatus, demoToggle:useCase.statuses.disabledStatus, demoToggle:useCase.passingContent.useCase.checkBox.label, demoToggle:useCase.passingContent.useCase.checkBox.helper, demoShowroomScreen:demoViaSpecifiedTxProp, demoShowroomScreen:demoViaSpecifiedTxProp, demoToggle:useCase.passingContent.useCase.checkBoxMultiLine.helper, demoToggle:useCase.passingContent.useCase.radioChangeSides.helper, demoToggle:useCase.passingContent.useCase.customCheckBox.label, demoToggle:useCase.passingContent.useCase.switch.label, demoToggle:useCase.passingContent.useCase.switch.helper, demoToggle:useCase.passingContent.useCase.switchAid.label, demoToggle:useCase.styling.outerWrapper, demoToggle:useCase.styling.innerWrapper, demoToggle:useCase.styling.inputDetail, demoToggle:useCase.styling.labelTx, demoToggle:useCase.styling.styleContainer, demoTextField:useCase.statuses.noStatus.label, demoTextField:useCase.statuses.noStatus.helper, demoTextField:useCase.statuses.noStatus.placeholder, demoTextField:useCase.statuses.error.label, demoTextField:useCase.statuses.error.helper, demoTextField:useCase.statuses.error.placeholder, demoTextField:useCase.statuses.disabled.label, demoTextField:useCase.statuses.disabled.helper, demoTextField:useCase.statuses.disabled.placeholder, demoTextField:useCase.passingContent.viaLabel.labelTx, demoTextField:useCase.passingContent.viaLabel.helper, demoTextField:useCase.passingContent.viaLabel.placeholder, demoShowroomScreen:demoViaSpecifiedTxProp, demoShowroomScreen:demoViaSpecifiedTxProp, demoShowroomScreen:demoViaSpecifiedTxProp, demoTextField:useCase.passingContent.rightAccessory.label, demoTextField:useCase.passingContent.rightAccessory.helper, demoTextField:useCase.passingContent.leftAccessory.label, demoTextField:useCase.passingContent.leftAccessory.helper, demoTextField:useCase.passingContent.supportsMultiline.label, demoTextField:useCase.passingContent.supportsMultiline.helper, demoTextField:useCase.styling.styleInput.label, demoTextField:useCase.styling.styleInput.helper, demoTextField:useCase.styling.styleInputWrapper.label, demoTextField:useCase.styling.styleInputWrapper.helper, demoTextField:useCase.styling.styleContainer.label, demoTextField:useCase.styling.styleContainer.helper, demoTextField:useCase.styling.styleLabel.label, demoTextField:useCase.styling.styleLabel.helper, demoTextField:useCase.styling.styleAccessories.label, demoTextField:useCase.styling.styleAccessories.helper, demoText:useCase.presets.default, demoText:useCase.presets.bold, demoText:useCase.presets.subheading, demoText:useCase.presets.heading, demoText:useCase.sizes.xs, demoText:useCase.sizes.sm, demoText:useCase.sizes.md, demoText:useCase.sizes.lg, demoText:useCase.sizes.xl, demoText:useCase.sizes.xxl, demoText:useCase.weights.light, demoText:useCase.weights.normal, demoText:useCase.weights.medium, demoText:useCase.weights.semibold, demoText:useCase.weights.bold, demoText:useCase.passingContent.viaText, demoText:useCase.passingContent.viaTx, demoShowroomScreen:lorem2Sentences, demoText:useCase.passingContent.children, demoText:useCase.passingContent.nestedChildren, demoText:useCase.passingContent.nestedChildren2, demoText:useCase.passingContent.nestedChildren3, demoText:useCase.passingContent.nestedChildren4, demoText:useCase.styling.text, demoText:useCase.styling.text2, demoText:useCase.styling.text3, demoListItem:useCase.height.defaultHeight, demoListItem:useCase.height.customHeight, demoListItem:useCase.height.textHeight, demoListItem:useCase.height.longText, demoListItem:useCase.separators.topSeparator, demoListItem:useCase.separators.topAndBottomSeparator, demoListItem:useCase.separators.bottomSeparator, demoListItem:useCase.icons.leftIcon, demoListItem:useCase.icons.rightIcon, demoListItem:useCase.icons.leftRightIcons, demoListItem:useCase.customLeftRight.customLeft, demoListItem:useCase.customLeftRight.customRight, demoListItem:useCase.passingContent.children, demoShowroomScreen:demoViaTxProp, demoListItem:useCase.passingContent.children, demoListItem:useCase.passingContent.nestedChildren1, demoListItem:useCase.passingContent.nestedChildren2, demoListItem:useCase.styling.styledText, demoListItem:useCase.styling.styledText, demoListItem:useCase.styling.styledContainer, demoListItem:useCase.styling.tintedIcons, demoHeader:useCase.actionIcons.leftIconTitle, demoHeader:useCase.actionIcons.rightIconTitle, demoHeader:useCase.actionIcons.bothIconsTitle, demoHeader:useCase.actionText.leftTxTitle, demoShowroomScreen:demoHeaderTxExample, demoHeader:useCase.actionText.rightTextTitle, demoHeader:useCase.customActionComponents.customLeftActionTitle, demoHeader:useCase.titleModes.centeredTitle, demoHeader:useCase.titleModes.flexTitle, demoHeader:useCase.styling.styledTitle, demoHeader:useCase.styling.styledWrapperTitle, demoHeader:useCase.styling.tintedIconsTitle, demoPodcastListScreen:title, demoPodcastListScreen:onlyFavorites, demoPodcastListScreen:accessibility.switch, demoPodcastListScreen:accessibility.cardHint, demoPodcastListScreen:accessibility.favoriteAction, demoPodcastListScreen:accessibility.unfavoriteIcon, demoPodcastListScreen:accessibility.favoriteIcon, demoPodcastListScreen:unfavoriteButton, demoPodcastListScreen:favoriteButton, demoDebugScreen:reportBugs, demoDebugScreen:title, demoDebugScreen:reactotron, common:logOut, demoCommunityScreen:title, demoCommunityScreen:tagLine, demoCommunityScreen:joinUsOnSlackTitle, demoCommunityScreen:joinUsOnSlack, demoCommunityScreen:joinSlackLink, demoCommunityScreen:makeIgniteEvenBetterTitle, demoCommunityScreen:makeIgniteEvenBetter, demoCommunityScreen:contributeToIgniteLink, demoCommunityScreen:theLatestInReactNativeTitle, demoCommunityScreen:theLatestInReactNative, demoCommunityScreen:reactNativeRadioLink, demoCommunityScreen:reactNativeNewsletterLink, demoCommunityScreen:reactNativeLiveLink, demoCommunityScreen:chainReactConferenceLink, demoCommunityScreen:hireUsTitle, demoCommunityScreen:hireUs, demoCommunityScreen:hireUsLink, demoNavigator:componentsTab, demoNavigator:communityTab, demoNavigator:podcastListTab, demoNavigator:podcastListTab, demoNavigator:debugTab, demoPodcastListScreen:accessibility.publishLabel, demoPodcastListScreen:accessibility.durationLabel
          Unused keys: common:cancel, common:back

@rsoaresdev
Copy link
Contributor Author

rsoaresdev commented Jan 18, 2025

Looks like translation keys for demo code (that's why it only returned 12 keys defined).
Probably CicleCI remove the demo translations but not the demo code itself, idk.

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.

4 participants