feat: add individual post page (#4)
* chore: add markdown lint to ignore no h1 heading in markdown files * chore: update example settings * chore: update example development env file * fix: remove fixed 100% height on container * feat: add individual post page * chore: update README.md * feat: add links to headings in post and add copy button to code blocks * tests: add tests for get post async method * tests: remove heading for test blogs * fix: only add links to headings in markdown body * tests: add tests for post page * tests: add test to check for blog content * docs: add jsdoc comments * chore: fix incorrect env variable format in example * fix: don't render anything if blog post is null * fix: don't render anything until posts are fetched * chore: add docker compose file for prod
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
namespace Blog.Tests.EndToEnd;
|
||||
|
||||
[TestFixture]
|
||||
public class PostTests : BlogTest
|
||||
{
|
||||
private const string TestPostSlug = "test-blog";
|
||||
private const string TestPostTitle = "Test Blog";
|
||||
private const string TestPostDate = "January 1, 2021";
|
||||
|
||||
[Test]
|
||||
public async Task Post_WhenNavigatedTo_ItShouldDisplayCorrectPageTitle()
|
||||
{
|
||||
await Page.GotoAsync($"/{TestPostSlug}");
|
||||
var title = await Page.TitleAsync();
|
||||
title.Should().Be($"{TestPostTitle} - journal");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Post_WhenNavigatedTo_ItShouldPostTitle()
|
||||
{
|
||||
await Page.GotoAsync($"/{TestPostSlug}");
|
||||
var heading = Page.GetByRole(AriaRole.Heading, new() { Name = TestPostTitle });
|
||||
await Expect(heading).ToBeVisibleAsync();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Post_WhenNavigatedTo_ItShouldDisplayPostDate()
|
||||
{
|
||||
await Page.GotoAsync($"/{TestPostSlug}");
|
||||
var date = Page.GetByText(TestPostDate);
|
||||
await Expect(date).ToBeVisibleAsync();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Post_WhenNavigatedTo_ItShouldDisplayContent()
|
||||
{
|
||||
await Page.GotoAsync($"/{TestPostSlug}");
|
||||
var content = Page.GetByRole(AriaRole.Article);
|
||||
await Expect(content).ToBeVisibleAsync();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Post_WhenNavigatedToAndPostDoesNotExist_ItShouldDisplay404()
|
||||
{
|
||||
await Page.GotoAsync("/non-existent-post");
|
||||
Page.Url.Should().Contain("/Error/404");
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
# Test Blog
|
||||
|
||||
```json meta
|
||||
{
|
||||
"title": "Another Test Blog",
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
# Test Blog
|
||||
|
||||
```json meta
|
||||
{
|
||||
"title": "Test Blog",
|
||||
|
||||
@@ -420,4 +420,197 @@ class FilePostServiceTests
|
||||
}
|
||||
]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task GetPostAsync_WhenCalledAndSlugDoesNotExist_ItShouldReturnNull()
|
||||
{
|
||||
_mockFileSystem
|
||||
.Setup(x => x.Directory.Exists(It.IsAny<string>()))
|
||||
.Returns(false);
|
||||
|
||||
var result = await _sut.GetPostAsync("slug");
|
||||
|
||||
result.Should().BeNull();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task GetPostAsync_WhenCalledAndPostExistsButContainsNoMetaData_ItShouldReturnNull()
|
||||
{
|
||||
_mockFileSystem
|
||||
.Setup(x => x.Directory.Exists(It.IsAny<string>()))
|
||||
.Returns(true);
|
||||
|
||||
_mockFileSystem
|
||||
.Setup(x => x.Path.Combine(It.IsAny<string>(), It.IsAny<string>()))
|
||||
.Returns("slug/INDEX.md");
|
||||
|
||||
_mockFileSystem
|
||||
.Setup(x => x.File.Exists(It.IsAny<string>()))
|
||||
.Returns(true);
|
||||
|
||||
_mockFileSystem
|
||||
.Setup(x => x.File.ReadAllTextAsync(It.IsAny<string>(), default))
|
||||
.ReturnsAsync("# Post Title");
|
||||
|
||||
var result = await _sut.GetPostAsync("slug");
|
||||
|
||||
result.Should().BeNull();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task GetPostAsync_WhenCalledAndPostContainsNoContent_ItShouldReturnNull()
|
||||
{
|
||||
_mockFileSystem
|
||||
.Setup(x => x.Directory.Exists(It.IsAny<string>()))
|
||||
.Returns(true);
|
||||
|
||||
_mockFileSystem
|
||||
.Setup(x => x.Path.Combine(It.IsAny<string>(), It.IsAny<string>()))
|
||||
.Returns("slug/INDEX.md");
|
||||
|
||||
_mockFileSystem
|
||||
.Setup(x => x.File.Exists(It.IsAny<string>()))
|
||||
.Returns(true);
|
||||
|
||||
_mockFileSystem
|
||||
.Setup(x => x.File.ReadAllTextAsync(It.IsAny<string>(), default))
|
||||
.ReturnsAsync(
|
||||
"""
|
||||
```json meta
|
||||
{
|
||||
"title": "Post Title",
|
||||
"date": "2021-01-01"
|
||||
}
|
||||
```
|
||||
"""
|
||||
);
|
||||
|
||||
var result = await _sut.GetPostAsync("slug");
|
||||
|
||||
result.Should().BeNull();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task GetPostAsync_WhenCalledAndPostExistsButContainsInvalidMetaData_ItShouldReturnNull()
|
||||
{
|
||||
_mockFileSystem
|
||||
.Setup(x => x.Directory.Exists(It.IsAny<string>()))
|
||||
.Returns(true);
|
||||
|
||||
_mockFileSystem
|
||||
.Setup(x => x.Path.Combine(It.IsAny<string>(), It.IsAny<string>()))
|
||||
.Returns("slug/INDEX.md");
|
||||
|
||||
_mockFileSystem
|
||||
.Setup(x => x.File.Exists(It.IsAny<string>()))
|
||||
.Returns(true);
|
||||
|
||||
_mockFileSystem
|
||||
.Setup(x => x.File.ReadAllTextAsync(It.IsAny<string>(), default))
|
||||
.ReturnsAsync(
|
||||
"""
|
||||
# Post Title
|
||||
|
||||
```json meta
|
||||
{
|
||||
"title": "Post Title",
|
||||
"isPublished": "true",
|
||||
"date": "2021-01-01"
|
||||
}
|
||||
```
|
||||
"""
|
||||
);
|
||||
|
||||
var result = await _sut.GetPostAsync("slug");
|
||||
|
||||
result.Should().BeNull();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task GetPostAsync_WhenCalledAndPostExistsButIsNotPublished_ItShouldReturnNull()
|
||||
{
|
||||
_mockFileSystem
|
||||
.Setup(x => x.Directory.Exists(It.IsAny<string>()))
|
||||
.Returns(true);
|
||||
|
||||
_mockFileSystem
|
||||
.Setup(x => x.Path.Combine(It.IsAny<string>(), It.IsAny<string>()))
|
||||
.Returns("slug/INDEX.md");
|
||||
|
||||
_mockFileSystem
|
||||
.Setup(x => x.File.Exists(It.IsAny<string>()))
|
||||
.Returns(true);
|
||||
|
||||
_mockFileSystem
|
||||
.Setup(x => x.File.ReadAllTextAsync(It.IsAny<string>(), default))
|
||||
.ReturnsAsync(
|
||||
"""
|
||||
# Post Title
|
||||
|
||||
```json meta
|
||||
{
|
||||
"title": "Post Title",
|
||||
"lead": "Post lead",
|
||||
"isPublished": false,
|
||||
"publishedAt": "2021-01-01"
|
||||
}
|
||||
```
|
||||
|
||||
Post content
|
||||
"""
|
||||
);
|
||||
|
||||
var result = await _sut.GetPostAsync("slug");
|
||||
|
||||
result.Should().BeNull();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task GetPostAsync_WhenCalledAndPostExists_ItShouldReturnPostWithContent()
|
||||
{
|
||||
_mockFileSystem
|
||||
.Setup(x => x.Directory.Exists(It.IsAny<string>()))
|
||||
.Returns(true);
|
||||
|
||||
_mockFileSystem
|
||||
.Setup(x => x.Path.Combine(It.IsAny<string>(), It.IsAny<string>()))
|
||||
.Returns("slug/INDEX.md");
|
||||
|
||||
_mockFileSystem
|
||||
.Setup(x => x.File.Exists(It.IsAny<string>()))
|
||||
.Returns(true);
|
||||
|
||||
_mockFileSystem
|
||||
.Setup(x => x.File.ReadAllTextAsync(It.IsAny<string>(), default))
|
||||
.ReturnsAsync(
|
||||
"""
|
||||
# Post Title
|
||||
|
||||
```json meta
|
||||
{
|
||||
"title": "Post Title",
|
||||
"lead": "Post lead",
|
||||
"isPublished": true,
|
||||
"publishedAt": "2021-01-01",
|
||||
"slug": "slug"
|
||||
}
|
||||
```
|
||||
|
||||
Post content
|
||||
"""
|
||||
);
|
||||
|
||||
var result = await _sut.GetPostAsync("slug");
|
||||
|
||||
result.Should().NotBeNull();
|
||||
result.Should().BeEquivalentTo(new PostWithContent
|
||||
{
|
||||
Title = "Post Title",
|
||||
Lead = "Post lead",
|
||||
IsPublished = true,
|
||||
PublishedAt = new DateTime(2021, 1, 1),
|
||||
Slug = "slug",
|
||||
Content = "<h1 id=\"post-title\">Post Title</h1>\n<p>Post content</p>\n",
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user