Skip to content

Built for people who want to own their automations. Join the waitlist for an invite.

Package listing

@kody/intercom

src/intercom.test.ts

163 lines · 6.2 KB · TypeScript
import assert from 'node:assert/strict'
import { readFileSync } from 'node:fs'
import { dirname, join } from 'node:path'
import test from 'node:test'
import { fileURLToPath } from 'node:url'
import {
	extractListItems,
	extractPageInfo,
	mapArticle,
	mapContact,
	mapConversation,
	mapMe,
} from './models.ts'
import {
	ACCESS_TOKEN_SETUP_URL,
	DEFAULT_TOKEN_SECRET,
	INTERCOM_CALLBACK_URL,
	OAUTH_CONNECT_URL,
	accessTokenSetupUrl,
	inferOperationFromPath,
	intercomUrl,
	mutationPreview,
	normalizeIntercomPath,
	oauthConnectUrl,
	permissionForOperation,
	resolveApiBaseUrl,
	resolveIntegrationName,
	resolveSecretName,
} from './setup.ts'

const here = dirname(fileURLToPath(import.meta.url))
const root = join(here, '..')

test('secret and integration names default and suffix accounts', () => {
	assert.equal(resolveIntegrationName(), 'intercom')
	assert.equal(resolveSecretName(), DEFAULT_TOKEN_SECRET)
	assert.equal(resolveIntegrationName({ account: 'default' }), 'intercom')
	assert.equal(resolveIntegrationName({ account: 'work' }), 'intercom-work')
	assert.equal(resolveSecretName({ account: 'work' }), 'intercomAccessToken-work')
	assert.equal(resolveIntegrationName({ integrationName: 'intercom-brand' }), 'intercom-brand')
	assert.equal(resolveSecretName({ secretName: 'intercomAccessToken-brand' }), 'intercomAccessToken-brand')
})

test('setup URLs are prefilled for OAuth and access token', () => {
	assert.match(OAUTH_CONNECT_URL, /provider=intercom/)
	assert.match(OAUTH_CONNECT_URL, /authorizeUrl=https%3A%2F%2Fapp\.intercom\.com%2Foauth/)
	assert.match(OAUTH_CONNECT_URL, /tokenUrl=https%3A%2F%2Fapi\.intercom\.io%2Fauth%2Feagle%2Ftoken/)
	assert.match(OAUTH_CONNECT_URL, /apiBaseUrl=https%3A%2F%2Fapi\.intercom\.io/)
	assert.match(OAUTH_CONNECT_URL, /allowedHosts=api\.intercom\.io/)
	assert.equal(INTERCOM_CALLBACK_URL, 'https://kody.codes/connect/oauth')
	assert.match(ACCESS_TOKEN_SETUP_URL, /name=intercomAccessToken/)
	assert.match(ACCESS_TOKEN_SETUP_URL, /allowedHosts=api\.intercom\.io/)
	assert.match(ACCESS_TOKEN_SETUP_URL, /scope=user/)
	assert.match(oauthConnectUrl('intercom-work'), /provider=intercom-work/)
	assert.match(accessTokenSetupUrl('intercomAccessToken-work'), /name=intercomAccessToken-work/)
	assert.equal(resolveApiBaseUrl({ region: 'eu' }), 'https://api.eu.intercom.io')
	assert.match(oauthConnectUrl('intercom', { region: 'eu' }), /app\.eu\.intercom\.com/)
	assert.equal(resolveApiBaseUrl({ region: 'au' }), 'https://api.au.intercom.io')
})

test('mutationPreview dryRun skips confirm', () => {
	const preview = mutationPreview(
		{ dryRun: true },
		{
			method: 'POST',
			path: '/contacts',
			url: 'https://api.intercom.io/contacts',
			body: { email: 'pat@example.com' },
		},
	)
	assert.deepEqual(preview, {
		dryRun: true,
		method: 'POST',
		path: '/contacts',
		url: 'https://api.intercom.io/contacts',
		body: { email: 'pat@example.com' },
	})
})

test('mutationPreview requires confirm for live writes', () => {
	assert.throws(
		() =>
			mutationPreview(
				{},
				{
					method: 'POST',
					path: '/articles',
					url: 'https://api.intercom.io/articles',
					body: { title: 'Reset your password' },
				},
			),
		/confirm: true/,
	)
	assert.equal(
		mutationPreview(
			{ confirm: true },
			{
				method: 'POST',
				path: '/articles',
				url: 'https://api.intercom.io/articles',
				body: { title: 'Reset your password' },
			},
		),
		null,
	)
})

test('paths stay on Intercom API hosts and infer operations', () => {
	assert.equal(normalizeIntercomPath('/contacts'), '/contacts')
	assert.match(intercomUrl('/contacts', { per_page: 5 }), /^https:\/\/api\.intercom\.io\/contacts\?per_page=5$/)
	assert.match(
		intercomUrl('/contacts', { per_page: 5 }, { region: 'eu' }),
		/^https:\/\/api\.eu\.intercom\.io\/contacts\?per_page=5$/,
	)
	assert.throws(() => normalizeIntercomPath('https://example.com/contacts'), /api\.intercom\.io/)
	assert.equal(inferOperationFromPath('GET', '/contacts'), 'contacts.read')
	assert.equal(inferOperationFromPath('POST', '/contacts/search'), 'contacts.read')
	assert.equal(inferOperationFromPath('POST', '/contacts'), 'contacts.write')
	assert.equal(inferOperationFromPath('POST', '/conversations/1/reply'), 'conversations.write')
	assert.equal(inferOperationFromPath('GET', '/articles/9'), 'articles.read')
	assert.equal(permissionForOperation('articles.write'), 'Read and Write Articles')
})

test('summaries stay generic and require ids from the payload', () => {
	assert.equal(mapContact({ email: 'pat@example.com' }), null)
	assert.equal(mapContact({ id: 'contact-from-caller', email: 'pat@example.com' })?.email, 'pat@example.com')
	assert.equal(mapConversation({ title: 'Help' }), null)
	assert.equal(mapConversation({ id: 'conversation-from-caller', state: 'open' })?.state, 'open')
	assert.equal(mapArticle({ title: 'Reset' }), null)
	assert.equal(mapArticle({ id: 'article-from-caller', title: 'Reset' })?.title, 'Reset')
	assert.deepEqual(mapMe({ id: 'admin-from-caller', type: 'admin', app: { region: 'US' } }).app.region, 'US')
	assert.deepEqual(
		extractListItems({ type: 'list', data: [{ id: '1' }] }),
		[{ id: '1' }],
	)
	assert.equal(
		extractPageInfo({
			pages: { next: { starting_after: 'cursor-from-caller' }, page: 1, per_page: 20, total_pages: 2 },
		}).startingAfter,
		'cursor-from-caller',
	)
})

test('package is public MIT official intercom with disabled job', () => {
	const manifest = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8'))
	assert.equal(manifest.name, '@kody/intercom')
	assert.equal(manifest.license, 'MIT')
	assert.equal(manifest.private, false)
	assert.equal(manifest.kody.id, 'intercom')
	assert.equal(manifest.kody.jobs['workspace-health'].enabled, false)
	const readme = readFileSync(join(root, 'README.md'), 'utf8')
	assert.match(readme, /## Intent/)
	assert.match(readme, /https:\/\/kody\.codes\/@kody\/intercom/)
	assert.match(readme, /\/account\/secrets\/new\?name=intercomAccessToken/)
	assert.match(readme, /\/connect\/oauth\?provider=intercom/)
	assert.doesNotMatch(readme, /\/community\/[0-9a-f-]{36}/)
	assert.doesNotMatch(readme, /hello@kentcdodds\.com/)
	assert.doesNotMatch(readme, /betterWithKent/)
	assert.doesNotMatch(readme, /[0-9a-f]{24}/)
	const icon = readFileSync(join(root, 'community-icon.svg'), 'utf8')
	assert.match(icon, /Official Intercom logomark/)
	assert.match(icon, /#1A1A19/i)
})