Skip to content

Commit

Permalink
feat(docs): test doc examples (#6149)
Browse files Browse the repository at this point in the history
Closes: #552

## Summary
This PR introduces doc example testing. In its current state you can include the keyword `example` in the Markdown code block identifier similar to how we use the `playground` ability.

If you create a Wing example in the docs and want to have it tested in CI when code changes are made, then all you need to do is something like `"```javascript example" ` and this will mark the code block for testing. 

You can also mark an example for failure if you expect it to not compile by using example meta data like so 
`"```javascript example{valid: false"`

I started adding this to many of our doc examples and found quite a few were broken, so a good chunk of the changes in this PR fix broken examples


- You can also run `pnpm turbo test:doc_examples` from the root of the project to only generate and run doc example tests.

## Further considerations
- How to test examples that require other examples? (I.E. library examples)
- How to test typescript examples

## Checklist

- [x] Title matches [Winglang's style guide](https://www.winglang.io/contributing/start-here/pull_requests#how-are-pull-request-titles-formatted)
- [x] Description explains motivation and solution
- [x] Tests added (always)
- [x] Docs updated (only required for features)
- [ ] Added `pr/e2e-full` label if this feature requires end-to-end testing

*By submitting this pull request, I confirm that my contribution is made under the terms of the [Wing Cloud Contribution License](https://github.com/winglang/wing/blob/main/CONTRIBUTION_LICENSE.md)*.
  • Loading branch information
hasanaburayyan authored Jun 12, 2024
1 parent 242ffda commit 73abe7d
Show file tree
Hide file tree
Showing 158 changed files with 2,527 additions and 240 deletions.
2 changes: 1 addition & 1 deletion docs/docs/01-start-here/02-getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ You can use the CLI to bootstrap a new project: Use the `new` command and then m
wing new empty
```

```js
```js example
bring cloud;

// define a queue, a bucket and a counter
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/02-concepts/00-cloud-oriented-programming.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ cloud without having to worry about the underlying infrastructure.
It's best explained through an example:


```js
```js example
bring cloud;

let queue = new cloud.Queue(timeout: 2m);
Expand Down
32 changes: 16 additions & 16 deletions docs/docs/02-concepts/01-preflight-and-inflight.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Your preflight code runs once, at compile time, and defines your application's i

For example, this code snippet defines a storage bucket using a class from the standard library:

```js playground
```js playground example
bring cloud;

let bucket = new cloud.Bucket();
Expand All @@ -42,7 +42,7 @@ Preflight code can be also used to configure services or set up more complex eve

In this code snippet, we've specified the bucket's contents will be publicly accessible, and it will be pre-populated with a file during the app's deployment (not while the app is running).

```js playground
```js playground example
bring cloud;

let bucket = new cloud.Bucket(public: true);
Expand All @@ -52,7 +52,7 @@ bucket.addObject("file1.txt", "Hello world!");
There are a few global functions with specific behaviors in preflight.
For example, adding a `log()` statement to your preflight code will result in Wing printing a message to the console after compilation.

```js
```js example
// hello.w
log("7 * 6 = {7 * 6}");
```
Expand Down Expand Up @@ -84,7 +84,7 @@ Let's walk through some examples.

Inflight code is always contained inside a block that starts with the word `inflight`.

```js
```js example
let greeting = inflight () => {
log("Hello from the cloud!");
};
Expand All @@ -93,7 +93,7 @@ let greeting = inflight () => {
Inflight code can call other inflight functions and methods.
For example, `cloud.Bucket` has an inflight method named `list()` that can be called inside inflight contexts:

```js playground
```js playground example
bring cloud;

let bucket = new cloud.Bucket();
Expand All @@ -110,7 +110,7 @@ Even though `bucket` is defined in preflight, it's okay to use its inflight meth

For an inflight function to actually get executed, it must be provided to an API that expects inflight code. For example, we can provide it to a `cloud.Function`:

```js playground
```js playground example
bring cloud;

let func = new cloud.Function(inflight () => {
Expand All @@ -133,7 +133,7 @@ firstObject(); // error: Cannot call into inflight phase while preflight
Likewise, inflight code cannot call preflight code, because preflight code has the capability to modify your application's infrastructure configuration, which is disallowed after deployment.
For example, since `addObject` is a preflight method, it cannot be called in inflight:

```js playground
```js playground example{valid: false}
bring cloud;

let bucket = new cloud.Bucket();
Expand All @@ -147,7 +147,7 @@ Instead, to insert an object into the bucket at runtime you would have to use an

Since a class's initializer is just a special kind of preflight function, it also isn't possible to initialize regular classes during preflight:

```js playground
```js playground example{valid: false}
bring cloud;

inflight () => {
Expand All @@ -163,7 +163,7 @@ A preflight class (the default kind of class) can contain both preflight and inf
Here's a class that models a queue that can replay its messages.
A `cloud.Bucket` stores the history of messages, and a `cloud.Counter` helps with sequencing each new message as it's added to the queue.

```js playground
```js playground example
bring cloud;

class ReplayableQueue {
Expand Down Expand Up @@ -202,7 +202,7 @@ Inflight classes are safe to create in inflight contexts.

For example, this inflight class can be created in an inflight contexts, and its methods can be called in inflight contexts:

```js playground
```js playground example
inflight () => {
class Person {
name: str;
Expand All @@ -213,7 +213,7 @@ inflight () => {
this.age = age;
}

inflight greet() {
pub inflight greet() {
log("Hello, {this.name}!");
}
}
Expand All @@ -230,7 +230,7 @@ While inflight code can't call preflight code, it's perfectly ok to reference da
For example, the `cloud.Api` class has a preflight field named `url`.
Since the URL is a string, it can be directly referenced inflight:

```js
```js example
bring cloud;
bring http;

Expand All @@ -254,7 +254,7 @@ new cloud.Function(checkEndpoint);
However, mutation to preflight data is not allowed.
This mean means that variables from preflight cannot be reassigned to, and mutable collections like `MutArray` and `MutMap` cannot be modified (they're turned into their immutable counterparts, `Array` and `Map`, respectively when accessed inflight).

```js playground
```js playground example{valid: false}
let var count = 3;
let names = MutArray<str>["John", "Jane", "Joe"];

Expand All @@ -271,7 +271,7 @@ inflight () => {

Preflight objects referenced inflight are called "lifted" objects:

```js playground
```js playground example
let preflight_str = "hello from preflight";
inflight () => {
log(preflight_str); // `preflight_str` is "lifted" into inflight.
Expand All @@ -281,7 +281,7 @@ inflight () => {
During the lifting process the compiler tries to figure out in what way the lifted objects are being used.
This is how Winglang generats least privilage permissions. Consider the case of lifting a [`cloud.Bucket`](../04-standard-library/cloud/bucket.md) object:

```js playground
```js playground example
bring cloud;
let bucket = new cloud.Bucket();
new cloud.Function(inflight () => {
Expand All @@ -294,7 +294,7 @@ In this example the compiler generates the correct _write_ access permissions fo
#### Explicit lift qualification
In some cases the compiler can't figure out (yet) the lift qualifications, and therefore will report an error:

```js playground
```js playground example{valid: false}
bring cloud;
let main_bucket = new cloud.Bucket() as "main";
let secondary_bucket = new cloud.Bucket() as "backup";
Expand Down
26 changes: 18 additions & 8 deletions docs/docs/02-concepts/02-application-tree.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,19 @@ The name is used to identify the resource in the Wing Console, and is used to de

The default name of a resource is the name of the class. The name can be overridden using the `as` syntax:

```js
```js example
bring cloud;

let bucket1 = new cloud.Bucket(); // default name is "cloud.Bucket"
let bucket2 = new cloud.Bucket() as "my-bucket";
```

The name of a resource needs to be unique within the scope it is defined.
New classes introduce new scopes, so the same name can be used for different resources in different classes.

```js
```js example
bring cloud;

class Group1 {
new() {
new cloud.Bucket() as "Store";
Expand All @@ -43,7 +47,7 @@ Instances of preflight classes define the application's construct tree. This way
In Wing this tree structure is automatically generated. Any class instantiated at the top level (global scope) is a child of the "root" node of of the tree.
Any class instantiated inside another class (in its constructor or one of its other preflight methods) will be placed as a child of that other class.

```js
```js example
class ThumbnailBucket {
//...
}
Expand All @@ -70,7 +74,9 @@ As mentioned in the [previous section](#instance-names) each instance must have
And the name will be automatically generated based on the class name.
So the tree shown above also shows the correct names for each infrastructure piece of our application.
You may query information about the construct tree using the `nodeof(someInstance)` intrinsic function:
```js
```js example
bring cloud;

let b = new cloud.Bucket();
log(nodeof(b).path); // Will log something like "/root/Bucket"
```
Expand All @@ -79,7 +85,7 @@ log(nodeof(b).path); // Will log something like "/root/Bucket"

You may define an explicit scope for an instance instead of using Wing's default of placing it inside the instance where it was created using the `in` keyword:

```js
```js example
class ThumbnailBucket {
//...
}
Expand Down Expand Up @@ -107,14 +113,16 @@ let defaultThumbnails = new ThumbnailBucket() as "defaultThumbs" in imageStorage
Preflight classes instantiated inside static methods, or instantiated inside constructors before `this` is available will use the
scope of the caller by default:

```js
```js example
bring cloud;

class Factory {
pub static make() {
new cloud.Bucket(); // We're in a static, so we don't know where to place this bucket
}
}

class MyBucket() {
class MyBucket {
new() {
Factory.make(); // Bucket will be placed inside `this` instance of `MyBucket`
}
Expand All @@ -132,7 +140,9 @@ new MyBucket();
Similarly, consider this case where we instantiate a class inside a parameter in a `super()` constructor call before the
the class is creates and its scope becomes valid:

```js
```js example
bring cloud;

class Base {
new(b: cloud.Bucket) {}
}
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/02-concepts/03-platforms.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ There might be times when you need to write code that is specific to a particula

With the Wing `util` library, you can access environment variables. The `WING_TARGET` environment variable contains the current platform target as it's value, which you can use to conditionally run target-specific code. See the example below:

```js playground
```js playground example
bring cloud;
bring util;

Expand Down
10 changes: 5 additions & 5 deletions docs/docs/02-concepts/04-tests.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Winglang incorporates a lightweight testing framework, which is built around the

You can create a test by adding the following code structure to any Winglang file (.w):

```ts wing
```ts wing example
test "<test-name>" {
// test code
}
Expand All @@ -21,7 +21,7 @@ If a test throws an exception (typically using the `assert` function), it's cons

Here's an example:

```ts playground
```ts playground example
// example.w
bring math;

Expand Down Expand Up @@ -50,7 +50,7 @@ Duration 0m0.54s

Every Winglang test is executed in complete isolation. Take a look at the following code:

```ts playground
```ts playground example
bring cloud;

let b = new cloud.Bucket();
Expand All @@ -73,7 +73,7 @@ In the first test (`bucket list should include created file`), a file is created

Consider the following example:

```ts playground
```ts playground example
bring cloud;
bring util;

Expand Down Expand Up @@ -140,7 +140,7 @@ Wing Console provides a straightforward method to run either a single test or al

Consider the following code:

```ts playground
```ts playground example{valid: false}
// example.w
bring cloud;

Expand Down
2 changes: 1 addition & 1 deletion docs/docs/04-standard-library/cloud/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ When a client invokes a route, the corresponding event handler function executes

The following example shows a complete REST API implementation using `cloud.Api`, `ex.Table` & `cloud.Counter`

```ts playground
```ts playground example
bring cloud;
bring ex;

Expand Down
8 changes: 4 additions & 4 deletions docs/docs/04-standard-library/cloud/bucket.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Unlike other kinds of storage like file storage, data is not stored in a hierarc

### Defining a bucket

```js
```js example
bring cloud;

let bucket = new cloud.Bucket(
Expand All @@ -38,7 +38,7 @@ let bucket = new cloud.Bucket(

If you have static data that you want to upload to the bucket each time your app is deployed, you can call the preflight method `addObject`:

```js
```js example
bring cloud;

let bucket = new cloud.Bucket();
Expand All @@ -48,7 +48,7 @@ bucket.addObject("my-file.txt", "Hello, world!");

### Using a bucket inflight

```js playground
```js playground example
bring cloud;

let bucket = new cloud.Bucket();
Expand Down Expand Up @@ -80,7 +80,7 @@ Use the `onEvent` method for responding to any event.

Each method creates a new `cloud.Function` resource which will be triggered by the given event type.

```js playground
```js playground example
bring cloud;

let store = new cloud.Bucket();
Expand Down
6 changes: 3 additions & 3 deletions docs/docs/04-standard-library/cloud/counter.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ The `cloud.Counter` resource represents a stateful container for one or more num

### Defining a counter

```js
```js example
bring cloud;

let counter = new cloud.Counter(
Expand All @@ -30,7 +30,7 @@ let counter = new cloud.Counter(

### Using a counter inflight

```js playground
```js playground example
bring cloud;

let counter = new cloud.Counter();
Expand All @@ -51,7 +51,7 @@ new cloud.Function(counterFunc);

### Using keys to manage multiple counter values

```js playground
```js playground example
bring cloud;

let counter = new cloud.Counter(initial: 100);
Expand Down
4 changes: 2 additions & 2 deletions docs/docs/04-standard-library/cloud/endpoint.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ The `cloud.Endpoint` represents a publicly accessible endpoint and outputs it as

## Usage

```ts playground
```ts playground example
bring cloud;

let endpoint = new cloud.Endpoint("https://example.com");
Expand Down Expand Up @@ -54,7 +54,7 @@ represents a publicly accessible endpoint and outputs it as part of the compilat

#### Initializers <a name="Initializers" id="@winglang/sdk.cloud.Endpoint.Initializer"></a>

```wing
```wing example
bring cloud;
new cloud.Endpoint("https://example.com");
Expand Down
Loading

0 comments on commit 73abe7d

Please sign in to comment.