Skip to content

Commit 1b883ab

Browse files
authored
Fix the python landing page (but keep hidden) - blocked (#334)
2 parents f480d5b + f29b5d6 commit 1b883ab

File tree

3 files changed

+196
-7
lines changed

3 files changed

+196
-7
lines changed

Diff for: selfie.dev/src/components/ButtonList.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export function ButtonList() {
3939
js
4040
</Button>
4141
</Link>
42-
<Link href="/other-platforms">
42+
<Link href="/py">
4343
<Button
4444
className={
4545
selectedLanguage === "py" ? pressedClasses : unPressedClasses
@@ -48,7 +48,7 @@ export function ButtonList() {
4848
py
4949
</Button>
5050
</Link>
51-
<Link href="/other-platforms">
51+
<Link href="/py">
5252
<Button
5353
className={
5454
selectedLanguage === "other-platforms"

Diff for: selfie.dev/src/components/IntroText.tsx

+22-5
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,16 @@ import clsx from "clsx";
22
import slugify from "@sindresorhus/slugify";
33
import { Button } from "./Button";
44
import Link from "next/link";
5+
import { useRouter } from "next/router";
6+
import { getPathParts } from "@/lib/languageFromPath";
57

6-
const THE_CONTEXT_WINDOW = "https://thecontextwindow.ai/p/temporarily-embarrassed-snapshots";
8+
const THE_CONTEXT_WINDOW =
9+
"https://thecontextwindow.ai/p/temporarily-embarrassed-snapshots";
710

811
export function IntroText() {
12+
const router = useRouter();
13+
const selectedLanguage = getPathParts(router.pathname).language;
14+
915
return (
1016
<div
1117
className={clsx([
@@ -35,7 +41,7 @@ export function IntroText() {
3541
<SectionLink title="literal" />, <SectionLink title="lensable" />
3642
<br /> and <SectionLink title="like a filesystem" />
3743
</p>
38-
<Link href="/jvm/get-started">
44+
<Link href={`/${selectedLanguage}/get-started`}>
3945
<Button
4046
className={clsx([
4147
"w-[154px]",
@@ -67,14 +73,25 @@ export function IntroText() {
6773
>
6874
<p>
6975
Snapshot testing is the <br />{" "}
70-
<a href={THE_CONTEXT_WINDOW} className="text-blue hover:underline cursor-pointer">fastest and most precise</a>
76+
<a
77+
href={THE_CONTEXT_WINDOW}
78+
className="text-blue hover:underline cursor-pointer"
79+
>
80+
fastest and most precise
81+
</a>
7182
<br />
7283
mechanism to{" "}
73-
<a href={THE_CONTEXT_WINDOW} className="text-red hover:underline cursor-pointer">
84+
<a
85+
href={THE_CONTEXT_WINDOW}
86+
className="text-red hover:underline cursor-pointer"
87+
>
7488
record <br /> and specify
7589
</a>{" "}
7690
the <br />
77-
<a href={THE_CONTEXT_WINDOW} className="text-green hover:underline cursor-pointer">
91+
<a
92+
href={THE_CONTEXT_WINDOW}
93+
className="text-green hover:underline cursor-pointer"
94+
>
7895
behavior of your <br />
7996
system and its <br />
8097
components

Diff for: selfie.dev/src/pages/py/index.mdx

+172
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
import { FooterCTA } from "@/components/FooterCTA/FooterCTA";
2+
import { NavHeading } from "@/components/NavHeading";
3+
4+
export const showHeroLinks = 'true';
5+
export const title = "Selfie Python Snapshot Testing";
6+
export const description = "Zero-config inline and disk snapshots for Python. Features garbage collection, filesystem-like APIs for snapshot data, and novel techniques for storytelling within test code.";
7+
8+
<NavHeading text="literal" popout="/py/get-started#quickstart" />
9+
10+
## NOT READY YET - WIP
11+
12+
This is a reasonable way to test.
13+
14+
```python
15+
@Test
16+
public void primesBelow100() {
17+
Assertions.assertThat(primesBelow(100)).startsWith(2, 3, 5, 7).endsWith(89, 97);
18+
}
19+
```
20+
21+
But oftentimes a more useful way to test is actually:
22+
23+
```python
24+
@Test
25+
public void testMcTestFace() {
26+
System.out.println(primesBelow(100));
27+
}
28+
```
29+
30+
With literal snapshots, you can `println` directly into your testcode, combining the speed and freedom of `println` with the repeatability and collaborative spirit of conventional assertions.
31+
32+
```python
33+
@Test
34+
public void primesBelow100() {
35+
expectSelfie(primesBelow(100).toString()).toBe_TODO();
36+
}
37+
```
38+
39+
When you run the test, selfie will automatically rewrite `_TODO()` into whatever it turned out to be.
40+
41+
```python
42+
@Test
43+
public void primesBelow100() {
44+
expectSelfie(primesBelow(100).toString())
45+
.toBe("[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]");
46+
}
47+
```
48+
49+
And from now on it's a proper assertion, but you didn't have to spend any time writing it. It's not only less work, but also more complete than the usual `.startsWith().endsWith()` rigamarole.
50+
51+
<NavHeading text="like-a-filesystem" popout="/py/get-started#disk" />
52+
53+
## NOT READY YET - WIP
54+
55+
That `primesBelow(100)` snapshot above is almost too long. Something bigger, such as `primesBelow(10_000)` is definitely too big. To handle this, selfie lets you put your snapshots on disk.
56+
57+
```python
58+
@Test
59+
public void gzipFavicon() {
60+
expectSelfie(get("/favicon.ico", ContentEncoding.GZIP)).toMatchDisk();
61+
}
62+
63+
@Test
64+
public void orderFlow() {
65+
expectSelfie(get("/orders")).toMatchDisk("initial");
66+
postOrder();
67+
expectSelfie(get("/orders")).toMatchDisk("ordered");
68+
}
69+
```
70+
71+
This will generate a snapshot file like so:
72+
73+
```html
74+
╔═ gzipFavicon ═╗ base64 length 12 bytes
75+
Umlja1JvbGwuanBn
76+
╔═ orderFlow/initial ═╗
77+
<html><body>
78+
<button>Submit order</button>
79+
</body></html>
80+
╔═ orderFlow/ordered ═╗
81+
<html><body>
82+
<p>Thanks for your business!</p>
83+
<details>
84+
<summary>Order information</summary>
85+
<p>Tracking #ABC123</p>
86+
</details>
87+
</body></html>
88+
```
89+
90+
Selfie's snapshot files `.ss` are simple to parse, just split them up on `\n╔═`. Escaping rules only come into play if the content you are escaping has lines that start with ``, and you can always use `selfie-lib` as a parser if you want.
91+
92+
You can treat your snapshot files as an output deliverable of your code, and use them as an input to other tooling.
93+
94+
<NavHeading text="lensable" popout="/py/facets" />
95+
96+
## NOT READY YET - WIP
97+
98+
A problem with the snapshots we've shown so far is that they are one dimensional. What about headers and cookies? What about the content the user actually sees, without all the markup? What if we could do this?
99+
100+
```
101+
╔═ orderFlow/initial [md] ═╗
102+
Submit order
103+
╔═ orderFlow/ordered [md] ═╗
104+
Thanks for your business!</p>
105+
```
106+
107+
Well, you can! Every snapshot has a *subject*, which is the main thing you are recording. And that subject can have any number of *facets*, which are named views of the subject from a different lens.
108+
109+
```python
110+
var html = "<html>..."
111+
var snapshot = Snapshot.of(html).plusFacet("md", HtmlToMdParser.parse(html))
112+
expectSelfie(snapshot).toMatchDisk()
113+
```
114+
115+
You can also use facets in combination with disk and inline literal snapshots to make your tests more like a story.
116+
117+
```python
118+
@Test
119+
public void orderFlow() {
120+
expectSelfie(get("/orders")).toMatchDisk("initial")
121+
.facet("md").toBe("Submit order");
122+
postOrder();
123+
expectSelfie(get("/orders")).toMatchDisk("ordered")
124+
.facet("md").toBe("Thanks for your business!");
125+
}
126+
```
127+
128+
Selfie's faceting is built around [Camera](https://kdoc.selfie.dev/selfie-lib/com.diffplug.selfie/-camera/), [Lens](https://kdoc.selfie.dev/selfie-lib/com.diffplug.selfie/-lens/), and [Snapshot](https://kdoc.selfie.dev/selfie-lib/com.diffplug.selfie/-snapshot/), whose API is roughly:
129+
130+
```python
131+
final class Snapshot {
132+
final SnapshotValue subject;
133+
final ImmutableSortedMap<String, SnapshotValue> facets;
134+
}
135+
interface Lens {
136+
Snapshot transform(Snapshot snapshot);
137+
}
138+
interface Camera<T> {
139+
Snapshot snapshot(T subject);
140+
default Camera<T> withLens(Lens lens) {
141+
// returns a new Camera which applies the given lens to every snapshot
142+
}
143+
}
144+
```
145+
146+
See the [facets section](/py/facets) for more details on how you can use Selfie for snapshot testing with Java, Kotlin, or any JVM language.
147+
148+
<NavHeading text="cacheable" popout="/py/cache" />
149+
150+
## NOT READY YET - WIP
151+
152+
Sometimes a test has a component which is slow, expensive, or non-deterministic. In cases like this, it can be useful to save the result of a previous execution of the API call, and use that as a mock for future tests.
153+
154+
```python
155+
var client = ExpensiveAiService();
156+
var chatResponse = cacheSelfie(() -> {
157+
return client.chat("What's your favorite number today?");
158+
}).toBe("Since it's March 14, my favorite number is π")
159+
// build other stuff with the chat response
160+
```
161+
162+
You can cache simple strings, but you can also cache typed API objects, binary data, or anything else you can serialize to a string or a byte array.
163+
164+
```python
165+
var imageBytes = cacheSelfieBinary(() -> {
166+
return client.generateImage("A robot making a self portrait");
167+
}).toBeFile("selfie.png")
168+
```
169+
170+
For more information on how to use `cacheSelfie`, see the [cache example](/py/cache).
171+
172+
<FooterCTA />

0 commit comments

Comments
 (0)