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

Setup base for PortingEmbed #1

Merged
merged 7 commits into from
Feb 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,15 @@ module.exports = {
},
],
'simple-import-sort/imports': 'error',
'@typescript-eslint/no-unused-vars': [
'error',
{
args: 'all',
argsIgnorePattern: '^_',
ignoreRestSiblings: true,
varsIgnorePattern: '^_',
caughtErrors: 'all',
},
],
},
}
19 changes: 19 additions & 0 deletions lib/PortingEmbed/PortingEmbed.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Porting } from '../types'

export type CustomizableEmbedProps = {
// TODO: add styling options
styleConfig?: {
foo?: string
}
}

type CoreEmbedProps = {
token: string
initialPorting: Porting
}

type PortingEmbedProps = CoreEmbedProps & CustomizableEmbedProps

export function PortingEmbed({ token: _, initialPorting }: PortingEmbedProps) {
return <div className="__gigsPortingEmbed">Hello {initialPorting.id}!</div>
}
22 changes: 22 additions & 0 deletions lib/PortingEmbed/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Porting Embed

## Usage

```ts
import { PortingEmbed } from '@gigs/gigs-embeds-js'

// Obtain a ConnectSession from your own backend
const connectSession = await fetchConnectSession()

const embed = await PortingEmbed(connectSession, { project: 'your-project' })
embed.mount(document.getElementById('gigsEmbedMount'))

// can also use a selector:
// embed.mount('#gigsEmbedMount')

// Here be more dragons

// ---
// index.html
<div id="gigsEmbedMount"></div>
```
20 changes: 20 additions & 0 deletions lib/PortingEmbed/__stories__/PortingEmbed.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { portingFactory } from '@/testing/factories/porting'

import { PortingEmbed } from '../PortingEmbed'

export default {
title: 'Porting Embed/Base',
component: PortingEmbed,
tags: ['autodocs'],
argTypes: {
token: { control: 'text' },
initialPorting: { control: 'object' },
},
}

export const Primary = {
args: {
token: 'abc:123',
initialPorting: portingFactory.build(),
},
}
12 changes: 12 additions & 0 deletions lib/PortingEmbed/__tests__/PortingEmbed.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { render, screen } from '@testing-library/preact'

import { portingFactory } from '@/testing/factories/porting'

import { PortingEmbed } from '../PortingEmbed'

it('gets the porting', () => {
const porting = portingFactory.params({ id: 'prt_123' }).build()
render(<PortingEmbed initialPorting={porting} token="abc:123" />)
const greeting = screen.getByText(/Hello prt_123/i)
expect(greeting).toBeInTheDocument()
})
181 changes: 181 additions & 0 deletions lib/PortingEmbed/__tests__/index.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
import { render } from '@testing-library/preact'

import { connectSessionFactory } from '@/testing/factories/connectSession'
import { portingFactory } from '@/testing/factories/porting'
import { subscriptionFactory } from '@/testing/factories/subscription'

import { PortingStatus } from '../../types'
import { PortingEmbed } from '../'

const project = 'test_project'

async function createFixtures() {
const subscription = await subscriptionFactory.create()
const connectSession = connectSessionFactory
.completePorting(subscription.id)
.build()
return connectSession
}

beforeEach(() => {
render(<div id="mount" />)
})

describe('mounting', () => {
it('mounts into a DOM selector', async () => {
const csn = await createFixtures()
const embed = await PortingEmbed(csn, { project })

embed.mount('#mount')
expect(document.querySelector('.__gigsPortingEmbed')).toBeInTheDocument()
})

it('mounts into a DOM element', async () => {
const csn = await createFixtures()
const embed = await PortingEmbed(csn, { project })

embed.mount(document.getElementById('mount')!)
expect(document.querySelector('.__gigsPortingEmbed')).toBeInTheDocument()
})
})

describe('updating', () => {
it('updates the embed', async () => {
const csn = await createFixtures()
const embed = await PortingEmbed(csn, { project })

embed.mount('#mount')
embed.update({})
expect(document.querySelector('.__gigsPortingEmbed')).toBeInTheDocument()
})

it('fails to update an unmounted embed', async () => {
const csn = await createFixtures()
const embed = await PortingEmbed(csn, { project })

expect(() => embed.update({})).toThrow(/an unmounted embed/i)
})
})

describe('unmounting', () => {
it('unmounts the embed', async () => {
const csn = await createFixtures()
const embed = await PortingEmbed(csn, { project })

embed.mount('#mount')
expect(document.getElementById('mount')).not.toBeEmptyDOMElement()
embed.unmount()
expect(document.getElementById('mount')).toBeEmptyDOMElement()
})

it('fails to unmount an unmounted embed', async () => {
const csn = await createFixtures()
const embed = await PortingEmbed(csn, { project })
expect(() => embed.unmount()).toThrow(/an unmounted embed/i)
})
})

describe('initialization', () => {
it('initializes with valid data', async () => {
const csn = await createFixtures()
const embed = await PortingEmbed(csn, { project })

expect(embed.mount).toBeDefined()
expect(embed.update).toBeDefined()
expect(embed.unmount).toBeDefined()
expect(embed.on).toBeDefined()
expect(embed.off).toBeDefined()
})

it('throws without a project', async () => {
const csn = await createFixtures()
// @ts-expect-error Assume the project is missing in a non-typechecked usage
const init = PortingEmbed(csn, {})
expect(init).rejects.toThrow(/NO_PROJECT/)
})

it('throws with the wrong ConnectSession', async () => {
expect(PortingEmbed(null, { project })).rejects.toThrow(/INVALID_SESSION/)
expect(PortingEmbed({}, { project })).rejects.toThrow(/INVALID_SESSION/)
expect(PortingEmbed({ secret: 'foo' }, { project })).rejects.toThrow(
/INVALID_SESSION/,
)
})

it('throws with a wrong intent', async () => {
const csn = connectSessionFactory
// @ts-expect-error Unsupported intent type
.params({ intent: { type: 'foo' } })
.build()
const init = PortingEmbed(csn, { project })
expect(init).rejects.toThrow(/INVALID_SESSION/)
})

it('throws with a non-existing subscription', async () => {
const csn = connectSessionFactory.completePorting('sub_404').build()
const init = PortingEmbed(csn, { project })
expect(init).rejects.toThrow(/NOT_FOUND/)
})

it('throws without a porting', async () => {
const sub = await subscriptionFactory.withoutPorting().create()
const csn = connectSessionFactory.completePorting(sub.id).build()
const init = PortingEmbed(csn, { project })
expect(init).rejects.toThrow(/NOT_FOUND/)
})

describe('with porting status', () => {
async function createWithStatus(status: PortingStatus) {
const porting = portingFactory.params({ status }).build()
const subscription = await subscriptionFactory
.associations({ porting })
.create()
const connectSession = connectSessionFactory
.completePorting(subscription.id)
.build()
return connectSession
}

it('initializes with informationRequired', async () => {
const csn = await createWithStatus('informationRequired')
const init = PortingEmbed(csn, { project })
expect(init).resolves.toBeDefined()
})

it('initializes with declined', async () => {
const csn = await createWithStatus('declined')
const init = PortingEmbed(csn, { project })
expect(init).resolves.toBeDefined()
})

it('throws with draft', async () => {
const csn = await createWithStatus('draft')
const init = PortingEmbed(csn, { project })
expect(init).rejects.toThrow(/UNSUPPORTED/)
})

it('throws with requested', async () => {
const csn = await createWithStatus('requested')
const init = PortingEmbed(csn, { project })
expect(init).rejects.toThrow(/UNSUPPORTED/)
})

it('throws with completed', async () => {
const csn = await createWithStatus('completed')
const init = PortingEmbed(csn, { project })
expect(init).rejects.toThrow(/UNSUPPORTED/)
})

it('throws with canceled', async () => {
const csn = await createWithStatus('canceled')
const init = PortingEmbed(csn, { project })
expect(init).rejects.toThrow(/UNSUPPORTED/)
})

it('throws with expired', async () => {
const csn = await createWithStatus('expired')
const init = PortingEmbed(csn, { project })
expect(init).rejects.toThrow(/UNSUPPORTED/)
})
})
})
Loading
Loading