Skip to content

Commit

Permalink
wip: add test suite and related_extensions array
Browse files Browse the repository at this point in the history
  • Loading branch information
mathiusj committed Dec 12, 2024
1 parent cb65c97 commit ff688bd
Show file tree
Hide file tree
Showing 6 changed files with 136 additions and 12 deletions.
24 changes: 24 additions & 0 deletions packages/app/src/cli/models/app/app.test-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1084,6 +1084,13 @@ export const testRemoteExtensionTemplates: ExtensionTemplate[] = [
path: 'discounts/rust/product-discounts/default',
},
],
relatedExtensions: [
{
type: 'ui_extension',
name: 'discount_function_settings',
directory: 'discount_ui/discount_ui',
},
],
},
{
identifier: 'order_discounts',
Expand Down Expand Up @@ -1112,6 +1119,23 @@ export const testRemoteExtensionTemplates: ExtensionTemplate[] = [
},
],
},
{
identifier: 'discount_function_settings',
name: 'Function - Discount settings',
defaultName: 'discount-function-settings',
group: 'Discounts and checkout',
supportLinks: [],
type: 'function',
url: 'https://github.com/Shopify/function-examples',
extensionPoints: [],
supportedFlavors: [
{
name: 'JavaScript',
value: 'vanilla-js',
path: 'discounts/javascript/discount-function-settings/default',
},
],
},
productSubscriptionUIExtensionTemplate,
themeAppExtensionTemplate,
]
Expand Down
5 changes: 5 additions & 0 deletions packages/app/src/cli/models/app/template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,9 @@ export interface ExtensionTemplate {
extensionPoints: string[]
supportedFlavors: ExtensionFlavor[]
url: string
relatedExtensions?: {
type: string
name: string
directory: string
}[]
}
9 changes: 7 additions & 2 deletions packages/app/src/cli/prompts/generate/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ export interface GenerateExtensionPromptOutput {
export interface GenerateExtensionContentOutput {
name: string
flavor?: ExtensionFlavorValue
relatedExtensions?: {
type: string
name: string
directory: string
}[]
}

export function buildChoices(extensionTemplates: ExtensionTemplate[], unavailableExtensions: ExtensionTemplate[] = []) {
Expand Down Expand Up @@ -74,7 +79,7 @@ const generateExtensionPrompts = async (
let extensionTemplates = options.extensionTemplates
let templateType = options.templateType
const extensionFlavor = options.extensionFlavor

console.log('EXTENSION TEMPLATES', extensionTemplates)

Check failure on line 82 in packages/app/src/cli/prompts/generate/extension.ts

View workflow job for this annotation

GitHub Actions / ESLint Report Analysis

packages/app/src/cli/prompts/generate/extension.ts#L82

[no-console] Unexpected console statement.
if (!templateType) {
if (extensionFlavor) {
extensionTemplates = extensionTemplates.filter((template) =>
Expand All @@ -94,7 +99,6 @@ const generateExtensionPrompts = async (
}

// eslint-disable-next-line @typescript-eslint/no-non-null-assertion

const extensionTemplate = extensionTemplates.find((template) => template.identifier === templateType)!

const name = options.name || (await promptName(options.directory, extensionTemplate.defaultName))

Check warning on line 104 in packages/app/src/cli/prompts/generate/extension.ts

View workflow job for this annotation

GitHub Actions / ESLint Report Analysis

packages/app/src/cli/prompts/generate/extension.ts#L104

[@typescript-eslint/prefer-nullish-coalescing] Prefer using nullish coalescing operator (`??`) instead of a logical or (`||`), as it is a safer operator.
Expand All @@ -103,6 +107,7 @@ const generateExtensionPrompts = async (
name,
flavor,
// extensionChildren [identifiers]
extensionChildren: extensionTemplate.relatedExtensions?.map((elem) => elem.name) ?? [],
}
// TODO: here perhaps return a flag, that lets us know that there are possible next steps

Check failure on line 112 in packages/app/src/cli/prompts/generate/extension.ts

View workflow job for this annotation

GitHub Actions / ESLint Report Analysis

packages/app/src/cli/prompts/generate/extension.ts#L112

[no-warning-comments] Unexpected 'todo' comment: 'TODO: here perhaps return a flag, that...'.

Expand Down
67 changes: 66 additions & 1 deletion packages/app/src/cli/services/generate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ describe('generate', () => {
│ • To preview this extension along with the rest of the project, run │
│ \`yarn shopify app dev\` │
│ │
╰─────────────────────────────────────────────────────────────────────────────╯
╰─────────────────────────────────────���────────────────────────────────────────╯
"
`)
})
Expand Down Expand Up @@ -207,6 +207,71 @@ describe('generate', () => {
// Then
await expect(got).rejects.toThrow(/Invalid template for extension type/)
})

test.only('creates related extensions in a shared directory with common configuration', async () => {
const outputInfo = await mockSuccessfulCommandExecution('product_discounts')

const productDiscountsTemplate = testRemoteExtensionTemplates.find(
(spec) => spec.identifier === 'product_discounts',
)
if (!productDiscountsTemplate) throw new Error('Product discounts template not found')

vi.mocked(generateExtensionPrompts).mockResolvedValue({
extensionTemplate: productDiscountsTemplate,
extensionContent: {
name: 'product_discounts',
flavor: 'vanilla-js',
relatedExtensions: [
{
type: 'function',
name: 'product_discounts',
directory: 'product_discounts_function/product_discounts_function',
},
{
type: 'ui_extension',
name: 'discount_function_settings',
directory: 'discount_ui/discount_ui',
},
],
},
})

// When
await generate({
directory: '/',
reset: false,
app,
remoteApp,
specifications,
developerPlatformClient,
})

// Then
expect(generateExtensionTemplate).toHaveBeenCalledTimes(2)

// Verify first extension (UI)
expect(generateExtensionTemplate).toHaveBeenCalledWith(
expect.objectContaining({
extensionContent: expect.objectContaining({
name: 'discount_function_settings',
type: 'ui_extension',
}),
}),
)

// Verify second extension (function)
expect(generateExtensionTemplate).toHaveBeenCalledWith(
expect.objectContaining({
extensionContent: expect.objectContaining({
name: 'product_discounts',
type: 'function',
}),
}),
)

// Verify shared configuration file was created
expect(outputInfo.info()).toContain('extensions/google-maps-validation')
})
})

async function mockSuccessfulCommandExecution(identifier: string, existingExtensions: ExtensionInstance[] = []) {
Expand Down
42 changes: 34 additions & 8 deletions packages/app/src/cli/services/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,39 @@ interface GenerateOptions {
async function generate(options: GenerateOptions) {
const {app, developerPlatformClient, remoteApp, specifications, template} = options

console.log('GENERATE', options)
// console.log('GENERATE', options)

const availableSpecifications = specifications.map((spec) => spec.identifier)
const extensionTemplates = await fetchExtensionTemplates(developerPlatformClient, remoteApp, availableSpecifications)

const promptOptions = await buildPromptOptions(extensionTemplates, specifications, app, options)
const promptAnswers = await generateExtensionPrompts(promptOptions)
// TODO: from the first prompt answers we can get the next steps

// Add related extension children for product discounts
if (promptAnswers.extensionTemplate.identifier === 'product_discounts') {
const settingsTemplate = extensionTemplates.find((template) => template.identifier === 'discount_function_settings')
if (settingsTemplate) {
const mockSettingsPromptAnswers: GenerateExtensionPromptOutput = {
extensionTemplate: settingsTemplate,
extensionContent: {
name: `${promptAnswers.extensionContent.name}-settings`,
flavor: 'react',
relatedExtensions: [],
},
}

// Generate the settings extension
const settingsGenerateOptions = buildGenerateOptions(
mockSettingsPromptAnswers,
app,
options,
developerPlatformClient,
)
await generateExtensionTemplate(settingsGenerateOptions)
}
}

console.log('PROMPT ANSWERS', promptAnswers)

Check failure on line 75 in packages/app/src/cli/services/generate.ts

View workflow job for this annotation

GitHub Actions / ESLint Report Analysis

packages/app/src/cli/services/generate.ts#L75

[no-console] Unexpected console statement.
// Call module related to that child extension
await saveAnalyticsMetadata(promptAnswers, template)
// Here we could check if the user wants to add a related extension
Expand All @@ -64,11 +89,11 @@ async function generate(options: GenerateOptions) {
// 4.2. Function settings is meant to be used with a discount function, would you like us to generate one for you?
// 4.3. What is the name of your function?
// Generates both extensions
// if (addPromptConfirmation) {
// // Generate related extensions
// const generateExtensionOptions = buildGenerateOptions(promptAnswers, app, options, developerPlatformClient)
// const generatedExtension = await generateExtensionTemplate(generateExtensionOptions)
// }
if (addPromptConfirmation) {
// Generate related extensions
const generateExtensionOptions = buildGenerateOptions(promptAnswers, app, options, developerPlatformClient)
const generatedExtension = await generateExtensionTemplate(generateExtensionOptions)

Check failure on line 95 in packages/app/src/cli/services/generate.ts

View workflow job for this annotation

GitHub Actions / ESLint Report Analysis

packages/app/src/cli/services/generate.ts#L95

[@typescript-eslint/no-unused-vars] 'generatedExtension' is assigned a value but never used. Allowed unused vars must match /^_/u.
}

renderSuccessMessage(generatedExtension, app.packageManager)
}
Expand All @@ -79,7 +104,7 @@ async function buildPromptOptions(
app: AppInterface,
options: GenerateOptions,
): Promise<GenerateExtensionPromptOptions> {
console.log('BUILDING PROMPT OPTIONS', arguments)
// console.log('BUILDING PROMPT OPTIONS', arguments)
const extensionTemplate = await handleTypeParameter(options.template, app, extensionTemplates, specifications)
validateExtensionFlavor(extensionTemplate, options.flavor)

Expand Down Expand Up @@ -120,6 +145,7 @@ function limitReached(app: AppInterface, specifications: ExtensionSpecification[

async function saveAnalyticsMetadata(promptAnswers: GenerateExtensionPromptOutput, typeFlag: string | undefined) {
const {extensionContent} = promptAnswers
console.log('SAVING METADATA', promptAnswers)

Check failure on line 148 in packages/app/src/cli/services/generate.ts

View workflow job for this annotation

GitHub Actions / ESLint Report Analysis

packages/app/src/cli/services/generate.ts#L148

[no-console] Unexpected console statement.
return metadata.addPublicMetadata(() => ({
cmd_scaffold_template_flavor: extensionContent.flavor,
cmd_scaffold_type: promptAnswers.extensionTemplate.identifier,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ export async function fetchExtensionTemplates(
): Promise<ExtensionTemplate[]> {
const remoteTemplates: ExtensionTemplate[] = await developerPlatformClient.templateSpecifications(app)
return remoteTemplates.filter((template) => {
console.log('TEMPLATE', template)
return availableSpecifications.includes(template.identifier) || availableSpecifications.includes(template.type)
})
}

0 comments on commit ff688bd

Please sign in to comment.