diff --git a/.gitignore b/.gitignore
index 7329a85..2c17e20 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,6 +2,9 @@
dist/
.output/
+# generated types
+.astro/
+
# dependencies
node_modules/
diff --git a/demo/src/pages/index.astro b/demo/src/pages/index.astro
index 01d2126..dbd9f98 100644
--- a/demo/src/pages/index.astro
+++ b/demo/src/pages/index.astro
@@ -19,6 +19,9 @@ import Base from '../layouts/Base.astro';
><LinkPreview/>
component examples
+
+ <Toot/>
component examples
+
Other examples
diff --git a/demo/src/pages/integration.mdx b/demo/src/pages/integration.mdx
index abac248..d056fff 100644
--- a/demo/src/pages/integration.mdx
+++ b/demo/src/pages/integration.mdx
@@ -23,3 +23,5 @@ https://vimeo.com/32001208
http://www.youtube.com/watch?v=Hoe-woAhq_k
https://astro.build/blog/welcome-world/
+
+https://mastodon.social/@sarah11918/112937553683639459
diff --git a/demo/src/pages/mastodon.astro b/demo/src/pages/mastodon.astro
new file mode 100644
index 0000000..c9d61a3
--- /dev/null
+++ b/demo/src/pages/mastodon.astro
@@ -0,0 +1,35 @@
+---
+import * as Component from 'astro-embed';
+import Base from '../layouts/Base.astro';
+---
+
+
+ Text-only toot
+
+ Toot with a preview card with an image
+
+ Toot with a preview card with no image
+
+ Toot with an image media attachment
+
+ Toot with a video media attachment
+
+ Toot with a gifv media attachment
+
+ Toot with an audio media attachment
+
+ Toot with custom emojis
+
+ Toot in RTL language
+
+
+
+
diff --git a/docs/.gitignore b/docs/.gitignore
index 6240da8..02f6e50 100644
--- a/docs/.gitignore
+++ b/docs/.gitignore
@@ -1,7 +1,5 @@
# build output
dist/
-# generated types
-.astro/
# dependencies
node_modules/
diff --git a/docs/src/assets/theme.css b/docs/src/assets/theme.css
index 0b733bf..4da2623 100644
--- a/docs/src/assets/theme.css
+++ b/docs/src/assets/theme.css
@@ -13,6 +13,7 @@
--sl-color-black: #19171c;
--tc-border-color: var(--sl-color-gray-5);
+ --mc-border-color: var(--sl-color-gray-5);
}
/* Light mode colors. */
:root[data-theme='light'] {
diff --git a/docs/src/content/docs/components/_examples/StyledToot.astro b/docs/src/content/docs/components/_examples/StyledToot.astro
new file mode 100644
index 0000000..8291eb6
--- /dev/null
+++ b/docs/src/content/docs/components/_examples/StyledToot.astro
@@ -0,0 +1,22 @@
+---
+import { Toot } from '@astro-community/astro-embed-mastodon';
+---
+
+
+
+
+
+
diff --git a/docs/src/content/docs/components/mastodon.mdx b/docs/src/content/docs/components/mastodon.mdx
new file mode 100644
index 0000000..7afc7e3
--- /dev/null
+++ b/docs/src/content/docs/components/mastodon.mdx
@@ -0,0 +1,98 @@
+---
+title: Toot
+description: Learn how to use the Astro Embed Toot component to embed Mastodon posts in your website
+---
+
+import { Tabs, TabItem } from '@astrojs/starlight/components';
+import { Tweet } from '@astro-community/astro-embed-twitter';
+import { Toot } from '@astro-community/astro-embed-mastodon';
+
+The `` component embeds posts from Mastodon in Astro projects.
+
+## Usage
+
+The `` component generates a static Mastodon card for a single Toot using [Mastodon’s API](https://docs.joinmastodon.org/client/intro/).
+
+
+
+```astro
+---
+import { Toot } from 'astro-embed';
+---
+
+
+```
+
+
+```mdx
+import { Toot } from 'astro-embed';
+
+
+```
+
+
+
+The above code produces the following result:
+
+
+
+
+
+:::note
+The `` component styles are intentionally minimal.
+They will match the font styles of your surrounding content. See [“Styling the Toot component”](#styling-the-toot-component) below for more details.
+:::
+
+## Styling the Toot component
+
+By default the `` card has minimal styling, which should adapt to your site’s font settings etc.
+
+You can customise it by targeting the `.mastodon-toot` class, for example:
+
+```css
+.mastodon-toot {
+ background: floralwhite;
+ color: darkblue;
+ font-family: cursive;
+ font-size: 1.25rem;
+ border: 0;
+}
+
+.mastodon-toot a {
+ color: purple;
+ font-weight: bold;
+}
+```
+
+The above styles would render `` like this:
+
+import StyledToot from './_examples/StyledToot.astro';
+
+
+
+### Custom properties API
+
+The `` component also supports a minimal API for controlling its styles by setting some CSS custom properties.
+
+```css
+:root {
+ /* Control the padding inside the Mastodon card. */
+ --mc-padding: 1em;
+ /* Set the border color of the Mastodon card. */
+ --mc-border-color: #cfd9de;
+}
+```
+
+## Standalone installation
+
+If you only need the `` component, you can install the package directly instead of the main `astro-embed` package:
+
+import { PackageManagers } from 'starlight-package-managers';
+
+
+
+The `` component can then be imported as:
+
+```js
+import { Toot } from '@astro-community/astro-embed-mastodon';
+```
diff --git a/docs/src/content/docs/getting-started.mdx b/docs/src/content/docs/getting-started.mdx
index d440892..626f83b 100644
--- a/docs/src/content/docs/getting-started.mdx
+++ b/docs/src/content/docs/getting-started.mdx
@@ -24,7 +24,7 @@ To use components in `.astro` files, import them in the component script and the
```astro
---
// src/components/Example.astro
-import { Tweet, Vimeo, YouTube } from 'astro-embed';
+import { Toot, Tweet, Vimeo, YouTube } from 'astro-embed';
---
@@ -32,6 +32,8 @@ import { Tweet, Vimeo, YouTube } from 'astro-embed';
+
+
```
### In MDX files
@@ -44,7 +46,7 @@ When using the [Astro MDX integration](https://docs.astro.build/en/guides/integr
title: My MDX page with embed components
---
-import { Tweet, Vimeo, YouTube } from 'astro-embed';
+import { Toot, Tweet, Vimeo, YouTube } from 'astro-embed';
Hey look! I can embed a tweet _in Markdown_!
@@ -54,6 +56,10 @@ Vimeo and YouTube videos work too :-)
+
+And even Mastodon toots!
+
+
```
### Auto-embed URLs in MDX
@@ -67,6 +73,7 @@ Learn how to set up the integration in the [“Auto-embed URLs”](/integration/
Astro Embed currently supports the following services:
+
diff --git a/docs/src/content/docs/index.mdx b/docs/src/content/docs/index.mdx
index 86f797d..063da61 100644
--- a/docs/src/content/docs/index.mdx
+++ b/docs/src/content/docs/index.mdx
@@ -46,6 +46,14 @@ import { Icon, Card, CardGrid } from '@astrojs/starlight/components';
+
+
+ Embed the contents of a Toot without client-side JavaScript.
+
+ [Toot docs](/components/mastodon/)
+
+
+
Embed the Open Graph media and metadata for any URL in your page.
diff --git a/package-lock.json b/package-lock.json
index b507047..7923386 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -90,6 +90,10 @@
"resolved": "packages/astro-embed-link-preview",
"link": true
},
+ "node_modules/@astro-community/astro-embed-mastodon": {
+ "resolved": "packages/astro-embed-mastodon",
+ "link": true
+ },
"node_modules/@astro-community/astro-embed-twitter": {
"resolved": "packages/astro-embed-twitter",
"link": true
@@ -12937,6 +12941,7 @@
"dependencies": {
"@astro-community/astro-embed-integration": "^0.7.1",
"@astro-community/astro-embed-link-preview": "^0.2.0",
+ "@astro-community/astro-embed-mastodon": "^0.0.1",
"@astro-community/astro-embed-twitter": "^0.5.4",
"@astro-community/astro-embed-vimeo": "^0.3.7",
"@astro-community/astro-embed-youtube": "^0.5.0"
@@ -12951,6 +12956,7 @@
"license": "MIT",
"dependencies": {
"@astro-community/astro-embed-link-preview": "^0.2.0",
+ "@astro-community/astro-embed-mastodon": "^0.0.1",
"@astro-community/astro-embed-twitter": "^0.5.4",
"@astro-community/astro-embed-vimeo": "^0.3.6",
"@astro-community/astro-embed-youtube": "^0.5.0",
@@ -12975,6 +12981,17 @@
"@astro-community/astro-embed-utils": "^0.1.1"
}
},
+ "packages/astro-embed-mastodon": {
+ "name": "@astro-community/astro-embed-mastodon",
+ "version": "0.0.1",
+ "license": "MIT",
+ "dependencies": {
+ "@astro-community/astro-embed-utils": "^0.1.3"
+ },
+ "peerDependencies": {
+ "astro": "^2.0.0 || ^3.0.0-beta || ^4.0.0-beta"
+ }
+ },
"packages/astro-embed-twitter": {
"name": "@astro-community/astro-embed-twitter",
"version": "0.5.4",
@@ -13007,7 +13024,7 @@
},
"packages/astro-embed-youtube": {
"name": "@astro-community/astro-embed-youtube",
- "version": "0.5.2",
+ "version": "0.5.3",
"license": "MIT",
"dependencies": {
"lite-youtube-embed": "^0.3.3"
diff --git a/packages/astro-embed-integration/package.json b/packages/astro-embed-integration/package.json
index 058993e..973c8cc 100644
--- a/packages/astro-embed-integration/package.json
+++ b/packages/astro-embed-integration/package.json
@@ -23,6 +23,7 @@
"homepage": "https://astro-embed.netlify.app/integration",
"dependencies": {
"@astro-community/astro-embed-link-preview": "^0.2.0",
+ "@astro-community/astro-embed-mastodon": "^0.0.1",
"@astro-community/astro-embed-twitter": "^0.5.4",
"@astro-community/astro-embed-vimeo": "^0.3.6",
"@astro-community/astro-embed-youtube": "^0.5.0",
diff --git a/packages/astro-embed-integration/remark-plugin.ts b/packages/astro-embed-integration/remark-plugin.ts
index 1707955..528d7d2 100644
--- a/packages/astro-embed-integration/remark-plugin.ts
+++ b/packages/astro-embed-integration/remark-plugin.ts
@@ -3,11 +3,13 @@ import twitterMatcher from '@astro-community/astro-embed-twitter/matcher';
import vimeoMatcher from '@astro-community/astro-embed-vimeo/matcher';
import youtubeMatcher from '@astro-community/astro-embed-youtube/matcher';
import linkPreviewMatcher from '@astro-community/astro-embed-link-preview/matcher';
+import mastodonMatcher from '@astro-community/astro-embed-mastodon/matcher';
const matchers = [
[twitterMatcher, 'Tweet'],
[vimeoMatcher, 'Vimeo'],
[youtubeMatcher, 'YouTube'],
+ [mastodonMatcher, 'Toot'],
/** The generic link matcher must be last otherwise it will override other URLs. */
[linkPreviewMatcher, 'LinkPreview'],
] as const;
diff --git a/packages/astro-embed-mastodon/MediaAttachment.astro b/packages/astro-embed-mastodon/MediaAttachment.astro
new file mode 100644
index 0000000..7e2ade0
--- /dev/null
+++ b/packages/astro-embed-mastodon/MediaAttachment.astro
@@ -0,0 +1,68 @@
+---
+interface Props {
+ media: MastodonMediaAttachment;
+}
+
+// https://docs.joinmastodon.org/entities/MediaAttachment/
+export interface MastodonMediaAttachment {
+ description: string | null;
+ meta: {
+ original?: {
+ height: number;
+ width: number;
+ };
+ small?: {
+ height: number;
+ width: number;
+ };
+ };
+ preview_url: string | null;
+ type: 'image' | 'gifv' | 'video' | 'audio' | 'unknown';
+ url: string;
+}
+
+const { media } = Astro.props;
+
+if (media.type === 'unknown') {
+ return;
+}
+---
+
+{
+ media.type === 'image' && media.preview_url && (
+
+
+
+ )
+}
+{
+ (media.type === 'video' || media.type === 'gifv') && (
+
+ )
+}
+{
+ media.type === 'audio' && (
+
+ )
+}
diff --git a/packages/astro-embed-mastodon/PreviewCard.astro b/packages/astro-embed-mastodon/PreviewCard.astro
new file mode 100644
index 0000000..96b8ea9
--- /dev/null
+++ b/packages/astro-embed-mastodon/PreviewCard.astro
@@ -0,0 +1,37 @@
+---
+interface Props {
+ card: MastodonPreviewCard;
+}
+
+// https://docs.joinmastodon.org/entities/PreviewCard/
+export interface MastodonPreviewCard {
+ description: string;
+ height: number;
+ image: string | null;
+ provider_name: string;
+ title: string;
+ url: string;
+ width: number;
+}
+
+const { card } = Astro.props;
+---
+
+
+ {
+ card.image && (
+
+ )
+ }
+
+ {card.provider_name && {card.provider_name}}
+ {card.title}
+ {card.description && {card.description}}
+
+
diff --git a/packages/astro-embed-mastodon/README.md b/packages/astro-embed-mastodon/README.md
new file mode 100644
index 0000000..f987146
--- /dev/null
+++ b/packages/astro-embed-mastodon/README.md
@@ -0,0 +1,5 @@
+# `@astro-community/astro-embed-mastodon`
+
+This package contains a component for embedding toots in Astro projects.
+
+[Read the `` component docs](https://astro-embed.netlify.app/components/mastodon/).
diff --git a/packages/astro-embed-mastodon/Toot.astro b/packages/astro-embed-mastodon/Toot.astro
new file mode 100644
index 0000000..f87ca6d
--- /dev/null
+++ b/packages/astro-embed-mastodon/Toot.astro
@@ -0,0 +1,129 @@
+---
+import { safeGet } from '@astro-community/astro-embed-utils';
+import PreviewCard, { type MastodonPreviewCard } from './PreviewCard.astro';
+import MediaAttachment, {
+ type MastodonMediaAttachment,
+} from './MediaAttachment.astro';
+import './Toot.css';
+
+export interface Props {
+ id: string;
+}
+const { id } = Astro.props;
+
+/**
+ * Thanks to @astrojs/starlight
+ * https://github.com/withastro/starlight/blob/8a861d16b586b019f61f30d93c61bdcd58e1503f/packages/starlight/utils/i18n.ts#L12
+ *
+ * A list of well-known right-to-left languages used as a fallback when determining the text
+ * direction of a locale is not supported by the `Intl.Locale` API in the current environment.
+ *
+ * @see getLocaleDir()
+ * @see https://en.wikipedia.org/wiki/IETF_language_tag#List_of_common_primary_language_subtags
+ */
+const wellKnownRTL = ['ar', 'fa', 'he', 'prs', 'ps', 'syc', 'ug', 'ur'];
+
+// Regex to extract a toot ID from a Mastodon status URL
+const idRegExp = /(\d+)$/;
+
+// Thanks to @astrojs/starlight
+// https://github.com/withastro/starlight/blob/8a861d16b586b019f61f30d93c61bdcd58e1503f/packages/starlight/utils/i18n.ts#L177-L188
+function getLocaleDir(locale: Intl.Locale): 'ltr' | 'rtl' {
+ if ('textInfo' in locale) {
+ // @ts-expect-error - `textInfo` is not typed but is available in v8 based environments.
+ return locale.textInfo.direction;
+ } else if ('getTextInfo' in locale) {
+ // @ts-expect-error - `getTextInfo` is not typed but is available in some non-v8 based environments.
+ return locale.getTextInfo().direction;
+ }
+ // Firefox does not support `textInfo` or `getTextInfo` yet so we fallback to a well-known list
+ // of right-to-left languages.
+ return wellKnownRTL.includes(locale.language) ? 'rtl' : 'ltr';
+}
+
+function extractID(url: string) {
+ const match = url.match(idRegExp);
+ return match?.[0];
+}
+
+async function fetchToot(id: string) {
+ const tootId = extractID(id);
+ if (!tootId) throw new Error('Invalid toot URL');
+ const data = (await safeGet(
+ `${new URL(id).origin}/api/v1/statuses/${tootId}`
+ )) as MastodonStatus | undefined;
+ return data
+ ? { ...data, dir: getLocaleDir(new Intl.Locale(data.language)) }
+ : undefined;
+}
+
+function replaceEmojis(text: string, emojis: MastodonCustomEmoji[]): string {
+ for (const emoji of emojis) {
+ text = text.replace(
+ new RegExp(`:${emoji.shortcode}:`, 'g'),
+ ``
+ );
+ }
+ return text;
+}
+
+// https://docs.joinmastodon.org/entities/Status/
+interface MastodonStatus {
+ account: {
+ acct: string;
+ display_name: string;
+ emojis: MastodonCustomEmoji[];
+ username: string;
+ };
+ card: MastodonPreviewCard | null;
+ content: string;
+ created_at: string;
+ emojis: MastodonCustomEmoji[];
+ language: string;
+ media_attachments: MastodonMediaAttachment[];
+ url: string;
+}
+
+// https://docs.joinmastodon.org/entities/Status/
+export interface MastodonCustomEmoji {
+ shortcode: string;
+ url: string;
+}
+
+const toot = await fetchToot(id);
+---
+
+{
+ toot && (
+
+
+
+ {toot.card && }
+ {toot.media_attachments.map((media) => (
+
+ ))}
+
+
+
+ )
+}
diff --git a/packages/astro-embed-mastodon/Toot.css b/packages/astro-embed-mastodon/Toot.css
new file mode 100644
index 0000000..d06bfec
--- /dev/null
+++ b/packages/astro-embed-mastodon/Toot.css
@@ -0,0 +1,58 @@
+.mastodon-toot {
+ border: 1px solid var(--mc-border-color, #cfd9de);
+ display: flex;
+ flex-direction: column;
+ gap: 1em;
+ overflow-wrap: anywhere;
+ padding: var(--mc-padding, 1em);
+}
+.mastodon-toot > div > :first-child {
+ margin-top: 0;
+}
+.mastodon-toot > div > :last-child {
+ margin-bottom: 0;
+}
+
+.mastodon-toot-card {
+ border: 1px solid var(--mc-border-color, #cfd9de);
+ display: flex;
+ flex-direction: column;
+}
+.mastodon-toot-card img {
+ width: 100%;
+}
+.mastodon-toot-card img + div {
+ border-top: 1px solid var(--mc-border-color, #cfd9de);
+ margin-top: 0;
+}
+.mastodon-toot-card div {
+ display: flex;
+ flex-direction: column;
+ gap: 0.75em;
+ padding: var(--mc-padding, 1em);
+}
+.mastodon-toot-card div span {
+ font-size: 0.875em;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.mastodon-toot-img,
+.mastodon-toot-video {
+ max-width: 100%;
+ height: auto;
+}
+.mastodon-toot-audio {
+ max-width: 100%;
+ object-fit: contain;
+}
+
+.mastodon-toot-emoji {
+ height: 1em;
+ /* Grabbed from Mastodon website. */
+ margin-block: -0.2ex 0.2ex;
+ object-fit: contain;
+ vertical-align: middle;
+ width: 1em;
+}
diff --git a/packages/astro-embed-mastodon/index.ts b/packages/astro-embed-mastodon/index.ts
new file mode 100644
index 0000000..b7edf72
--- /dev/null
+++ b/packages/astro-embed-mastodon/index.ts
@@ -0,0 +1 @@
+export { default as Toot } from './Toot.astro';
diff --git a/packages/astro-embed-mastodon/matcher.ts b/packages/astro-embed-mastodon/matcher.ts
new file mode 100644
index 0000000..eb2452c
--- /dev/null
+++ b/packages/astro-embed-mastodon/matcher.ts
@@ -0,0 +1,14 @@
+// Thanks to eleventy-plugin-embed-mastodon
+// https://github.com/inframanufaktur/eleventy-plugin-embed-mastodon/blob/f8c1687abf1f88a6351e53e38f91aff0c95a5cb2/.eleventy.js#L46-L47
+const urlPattern =
+ /(?:https:\/\/)?([\w\d-]*?.?[\w\d-]*.[a-z]*\/@[\w\d_]*(?:@[\w\d]*?.?[\w\d]*.[a-z]*)?\/)(\d+)/;
+
+/**
+ * Return a Toot URL from a URL if it matches the pattern.
+ * @param url URL to test
+ * @returns A Toot URL or undefined if none matched
+ */
+export default function urlMatcher(url: string): string | undefined {
+ const match = url.match(urlPattern);
+ return match?.[0];
+}
diff --git a/packages/astro-embed-mastodon/package.json b/packages/astro-embed-mastodon/package.json
new file mode 100644
index 0000000..55f3781
--- /dev/null
+++ b/packages/astro-embed-mastodon/package.json
@@ -0,0 +1,41 @@
+{
+ "name": "@astro-community/astro-embed-mastodon",
+ "version": "0.0.1",
+ "description": "Component to easily embed Toots on your Astro site",
+ "type": "module",
+ "exports": {
+ ".": "./index.ts",
+ "./matcher": "./matcher.ts"
+ },
+ "files": [
+ "index.ts",
+ "matcher.ts",
+ "MediaAttachment.astro",
+ "PreviewCard.astro",
+ "Toot.astro",
+ "Toot.css"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/delucis/astro-embed",
+ "directory": "packages/astro-embed-mastodon"
+ },
+ "keywords": [
+ "astro",
+ "astro-component",
+ "embeds",
+ "mastodon"
+ ],
+ "author": "delucis",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/delucis/astro-embed/issues"
+ },
+ "homepage": "https://astro-embed.netlify.app/components/mastodon/",
+ "peerDependencies": {
+ "astro": "^2.0.0 || ^3.0.0-beta || ^4.0.0-beta"
+ },
+ "dependencies": {
+ "@astro-community/astro-embed-utils": "^0.1.3"
+ }
+}
diff --git a/packages/astro-embed/index.ts b/packages/astro-embed/index.ts
index 24ba8c8..436e061 100644
--- a/packages/astro-embed/index.ts
+++ b/packages/astro-embed/index.ts
@@ -2,3 +2,4 @@ export { Tweet } from '@astro-community/astro-embed-twitter';
export { YouTube } from '@astro-community/astro-embed-youtube';
export { Vimeo } from '@astro-community/astro-embed-vimeo';
export { LinkPreview } from '@astro-community/astro-embed-link-preview';
+export { Toot } from '@astro-community/astro-embed-mastodon';
diff --git a/packages/astro-embed/package.json b/packages/astro-embed/package.json
index e7b70a9..5f63e42 100644
--- a/packages/astro-embed/package.json
+++ b/packages/astro-embed/package.json
@@ -22,7 +22,8 @@
"embeds",
"twitter",
"vimeo",
- "youtube"
+ "youtube",
+ "mastodon"
],
"author": "delucis",
"license": "MIT",
@@ -33,6 +34,7 @@
"dependencies": {
"@astro-community/astro-embed-link-preview": "^0.2.0",
"@astro-community/astro-embed-integration": "^0.7.1",
+ "@astro-community/astro-embed-mastodon": "^0.0.1",
"@astro-community/astro-embed-twitter": "^0.5.4",
"@astro-community/astro-embed-vimeo": "^0.3.7",
"@astro-community/astro-embed-youtube": "^0.5.0"
diff --git a/tests/astro-embed-mastodon.mjs b/tests/astro-embed-mastodon.mjs
new file mode 100644
index 0000000..6d6546a
--- /dev/null
+++ b/tests/astro-embed-mastodon.mjs
@@ -0,0 +1,124 @@
+import { test } from 'uvu';
+import * as assert from 'uvu/assert';
+import { renderDOM, renderScreen } from './utils/render.mjs';
+
+test('it should render a toot', async () => {
+ const screen = await renderScreen(
+ './packages/astro-embed-mastodon/Toot.astro',
+ { id: 'https://mastodon.social/@sarah11918/112937553683639459' }
+ );
+ screen.getByText(/@sarah11918/);
+ screen.getByText('August 10, 2024');
+});
+
+test('it should render a toot with a preview card with no image', async () => {
+ const { document } = await renderDOM(
+ './packages/astro-embed-mastodon/Toot.astro',
+ {
+ id: 'https://fosstodon.org/@yabellini/112905997660025406',
+ }
+ );
+ const card = document.querySelector('.mastodon-toot-card');
+ assert.ok(card);
+ assert.match(card.textContent ?? '', /Friend Camp/);
+ const image = card.querySelector('img');
+ assert.not(image);
+});
+
+test('it should render a toot with a preview card with an image', async () => {
+ const { document } = await renderDOM(
+ './packages/astro-embed-mastodon/Toot.astro',
+ {
+ id: 'https://mastodon.social/@sarah11918/112954791910873136',
+ }
+ );
+ const card = document.querySelector('.mastodon-toot-card');
+ assert.ok(card);
+ assert.match(card.textContent ?? '', /50 Docs tips in 50 days/);
+ const image = card.querySelector('img');
+ assert.ok(image);
+});
+
+test('it should render a toot with an image media attachment', async () => {
+ const { document } = await renderDOM(
+ './packages/astro-embed-mastodon/Toot.astro',
+ {
+ id: 'https://mastodon.social/@sarah11918/112938341053568673',
+ }
+ );
+ const image = document.querySelector('img.mastodon-toot-img');
+ assert.ok(image);
+});
+
+test('it should render a toot with a video media attachment', async () => {
+ const { document } = await renderDOM(
+ './packages/astro-embed-mastodon/Toot.astro',
+ {
+ id: 'https://m.webtoo.ls/@astro/112956966118149936',
+ }
+ );
+ const video = document.querySelector('video.mastodon-toot-video');
+ assert.ok(video);
+});
+
+test('it should render a toot with a gifv media attachment', async () => {
+ const { document } = await renderDOM(
+ './packages/astro-embed-mastodon/Toot.astro',
+ {
+ id: 'https://m.webtoo.ls/@astro/112914830775250637',
+ }
+ );
+ const video = document.querySelector('video.mastodon-toot-video');
+ assert.ok(video);
+});
+
+test('it should render a toot with an audio media attachment', async () => {
+ const { document } = await renderDOM(
+ './packages/astro-embed-mastodon/Toot.astro',
+ {
+ id: 'https://nahe.social/@yuliyan/112948405749089071',
+ }
+ );
+ const audio = document.querySelector('audio.mastodon-toot-audio');
+ assert.ok(audio);
+});
+
+test('it should render a toot with custom emojis', async () => {
+ const { document } = await renderDOM(
+ './packages/astro-embed-mastodon/Toot.astro',
+ {
+ id: 'https://mastodon.social/@onokoto/112881577072270247',
+ }
+ );
+ const emoji = document.querySelector('img.mastodon-toot-emoji');
+ assert.ok(emoji);
+ assert.equal(emoji.getAttribute('alt'), ':mastodon:');
+});
+
+// This test is a bit flaky, as usernames can be changed at any time and the emoji might no longer be present.
+test.skip('it should render a toot with a username including custom emojis', async () => {
+ const { document } = await renderDOM(
+ './packages/astro-embed-mastodon/Toot.astro',
+ {
+ id: 'https://nahe.social/@yuliyan/112948405749089071',
+ }
+ );
+ const emoji = document.querySelector('img.mastodon-toot-emoji');
+ assert.ok(emoji);
+ assert.equal(emoji.getAttribute('alt'), ':questified:');
+});
+
+test('it should render in RTL languages', async () => {
+ const { document } = await renderDOM(
+ './packages/astro-embed-mastodon/Toot.astro',
+ {
+ id: 'https://mastodon.social/@Panahifarah@khiar.net/112123163633804419',
+ }
+ );
+ const content = document.querySelector('blockquote > div');
+ assert.ok(content);
+ assert.equal(content.getAttribute('lang'), 'fa');
+ assert.equal(content.getAttribute('dir'), 'rtl');
+});
+
+test.run();