Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: adds landing page scaffolding #641

Merged
22 changes: 22 additions & 0 deletions components/blocks/Landing/Landing.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { mountSuspended } from '@nuxt/test-utils/runtime'
import Landing from './Landing.vue'
import LandingAbout from './LandingAbout.vue'
import LandingLeaderboards from './LandingLeaderboards.vue'

const mockSuccessGetLeaderboards = vi.fn(() => Promise.resolve({ ok: true }))
vi.mock('lib/api/Leaderboards', () => ({
Leaderboards: function Leaderboards() {
this.getLeaderboards = mockSuccessGetLeaderboards
},
}))

describe('Landing Component', () => {
it('should render without crashing', async () => {
const wrapper = await mountSuspended(Landing)

expect(wrapper.isVisible()).toBe(true)
expect(wrapper.getComponent(LandingAbout).isVisible()).toBe(true)
expect(wrapper.getComponent(LandingLeaderboards).isVisible()).toBe(true)
expect(mockSuccessGetLeaderboards).toHaveBeenCalledOnce()
})
})
20 changes: 20 additions & 0 deletions components/blocks/Landing/Landing.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<script lang="ts" setup>
import LandingLeaderboards from './LandingLeaderboards.vue'
import LandingAbout from './LandingAbout.vue'
import { useGetLeaderboards } from '~/composables/api'

import Loader from 'blocks/Loader/Loader.vue'

const leaderboards = await useGetLeaderboards()
</script>

<template>
<div>
<h2 class="mb-4 text-3xl font-bold">Games</h2>
<div class="flex flex-col lg:flex-row">
<Loader v-if="leaderboards.loading"></Loader>
<LandingLeaderboards :leaderboards="leaderboards.data || []" />
<LandingAbout />
</div>
</div>
</template>
13 changes: 13 additions & 0 deletions components/blocks/Landing/LandingAbout.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { mountSuspended } from '@nuxt/test-utils/runtime'
import Landing from './LandingAbout.vue'

describe('LandingAbout Component', () => {
it('should render without crashing', async () => {
const wrapper = await mountSuspended(Landing)

expect(wrapper.isVisible()).toBe(true)

const text = wrapper.getComponent('div#landing-about').text()
expect(text).toContain('About Leaderboards.gg')
})
})
44 changes: 44 additions & 0 deletions components/blocks/Landing/LandingAbout.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<script lang="ts" setup></script>

<template>
<div id="landing-about" class="lg:max-w-[330px] lg:pl-4">
<aside class="bg-gray-200 p-4">
<h2 class="mb-4 text-xl font-bold">About Leaderboards.gg</h2>
<div class="mb-4">
<h3 class="mb-2 text-lg font-bold">Explanation 1</h3>
<p class="mb-2 text-sm">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur
nibh turpis, convallis ut sodales vel, faucibus a libero. Nunc mi
odio, aliquet eget justo in, hendrerit rutrum velit. Nullam interdum
nunc vel quam congue, eget venenatis dui vehicula. Praesent mi tortor,
accumsan commodo massa...
</p>
<a href="#" class="text-sm text-blue-500 underline">Link Out</a>
</div>
<div class="mb-4">
<h3 class="mb-2 text-lg font-bold">Explanation 2</h3>
<p class="mb-2 text-sm">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur
nibh turpis, convallis ut sodales vel, faucibus a libero. Nunc mi
odio, aliquet eget justo in, hendrerit rutrum velit. Nullam interdum
nunc vel quam congue, eget venenatis dui vehicula. Praesent mi tortor,
accumsan commodo massa...
</p>
<a href="#" class="text-sm text-blue-500 underline">Link Out</a>
</div>
<div class="mb-4">
<h3 class="mb-2 text-lg font-bold">Explanation 3</h3>
<p class="mb-2 text-sm">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur
nibh turpis, convallis ut sodales vel, faucibus a libero. Nunc mi
odio, aliquet eget justo in, hendrerit rutrum velit. Nullam interdum
nunc vel quam congue, eget venenatis dui vehicula. Praesent mi tortor,
accumsan commodo massa...
</p>
<a href="#" class="text-sm text-blue-500 underline">Link Out</a>
</div>
</aside>
</div>
</template>

<style></style>
59 changes: 59 additions & 0 deletions components/blocks/Landing/LandingLeaderboards.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { mountSuspended } from '@nuxt/test-utils/runtime'
import Landing from './LandingLeaderboards.vue'
import type { LeaderboardViewModel } from '~/lib/api/data-contracts'

const games: LeaderboardViewModel[] = [
{
id: 1,
name: 'Getting Over It With Bennet Foddy',
slug: 'slug-2',
info: null,
createdAt: '2024-11-02T22:11:08+0000',
updatedAt: '2024-11-02T22:11:08+0000',
deletedAt: null,
categories: [],
},
{
id: 2,
name: 'Getting Over It With Bennet Foddy',
slug: 'slug-2',
info: null,
createdAt: '2024-11-02T22:11:08+0000',
updatedAt: '2024-11-02T22:11:08+0000',
deletedAt: null,
categories: [],
},
{
id: 3,
name: 'Getting Over It With Bennet Foddy',
slug: 'slug-3',
info: null,
createdAt: '2024-11-02T22:11:08+0000',
updatedAt: '2024-11-02T22:11:08+0000',
deletedAt: null,
categories: [],
},
]

describe('LandingLeaderboards Component', () => {
afterEach(() => {
vi.restoreAllMocks()
})

it('should render without crashing', async () => {
const wrapper = await mountSuspended(Landing, {
props: {
leaderboards: games,
},
})

expect(wrapper.isVisible()).toBe(true)

wrapper
.getComponent('div#landing-leaderboards')
.findAllComponents('div')
.forEach((c, i) => {
expect(c.text()).toBe(games[i])
})
})
})
33 changes: 33 additions & 0 deletions components/blocks/Landing/LandingLeaderboards.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<script setup lang="ts">
import type { LeaderboardViewModel } from '~/lib/api/data-contracts'
import { useLocalePath } from '#imports'
const localePath = useLocalePath()

interface LandingLeaderboardsProps {
leaderboards: LeaderboardViewModel[]
}

defineProps<LandingLeaderboardsProps>()
</script>
<template>
<div
id="landing-leaderboards"
class="mb-4 grid w-full grid-cols-1 content-start items-start gap-4 lg:grid-cols-2"
>
<NuxtLink
v-for="leaderboard in leaderboards"
:key="leaderboard.id"
class="mb-2 h-full content-center rounded-lg bg-gray-200 p-8 text-blue-500 underline hover:cursor-pointer"
:to="
localePath({
name: 'board-slug',
params: { slug: leaderboard.slug },
})
"
>
<span class="contents break-words font-bold">
{{ leaderboard?.name }}
</span>
</NuxtLink>
</div>
</template>
1 change: 1 addition & 0 deletions composables/api/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export { default as useChangePassword } from './useChangePassword'
export { default as useConfirmAccount } from './useConfirmAccount'
export { default as useGetLeaderboardBySlug } from './useGetLeaderboardBySlug'
export { default as useGetLeaderboards } from './useGetLeaderboards'
export { default as useGetUser } from './useGetUser'
export { default as useLoginUser } from './useLoginUser'
export { default as useLogoutUser } from './useLogoutUser'
Expand Down
40 changes: 40 additions & 0 deletions composables/api/useGetLeaderboards/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { ref } from 'vue'
import {
useApi,
type ApiResponse,
type optionalParameters,
} from 'composables/useApi'
import { Leaderboards } from 'lib/api/Leaderboards'
import type { LeaderboardViewModel } from 'lib/api/data-contracts'
import { useRuntimeConfig } from '#imports'

export default async function useGetLeaderboards(
opts: optionalParameters<LeaderboardViewModel[]> = {},
): Promise<ApiResponse<LeaderboardViewModel[]>> {
const { onError, onOkay } = opts
const responseData = ref<LeaderboardViewModel[]>([
{
categories: [],
id: -1,
name: '',
slug: '',
info: null,
createdAt: '',
updatedAt: null,
deletedAt: null,
},
])

const leaderboardClient = new Leaderboards({
baseUrl: useRuntimeConfig().public.backendBaseUrl,
})

return await useApi<LeaderboardViewModel[]>(
async () => await leaderboardClient.getLeaderboards({}),
{
onError,
onOkay,
responseData,
},
)
}
21 changes: 21 additions & 0 deletions composables/api/useGetLeaderboards/useGetLeaderboards.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import useGetLeaderboards from '.'

const mockSuccessGetLeaderboards = vi.fn(() => Promise.resolve({ ok: true }))

describe('useGetLeaderboard', () => {
describe('when everything is successful', () => {
const queryParams = {}
it('creates a GET request to fetch the a list of leaderboards', async () => {
vi.mock('lib/api/Leaderboards', () => ({
Leaderboards: function Leaderboards() {
this.getLeaderboards = mockSuccessGetLeaderboards
},
}))

await useGetLeaderboards()

expect(mockSuccessGetLeaderboards).toBeCalledTimes(1)
expect(mockSuccessGetLeaderboards).toBeCalledWith(queryParams)
})
})
})
1 change: 1 addition & 0 deletions pages/board/[slug].vue
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<script setup lang="ts">
import { useRoute } from '#imports'
import LeaderboardInfo from 'blocks/LeaderboardInfo/LeaderboardInfo.vue'
import Loader from 'blocks/Loader/Loader.vue'
import { useGetLeaderboardBySlug } from '~/composables/api'
Expand Down
6 changes: 1 addition & 5 deletions pages/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,6 @@ describe('/index', () => {

it('should render the placeholder text', async () => {
const IndexWrapper = await mountSuspended(index)
expect(
IndexWrapper.html().includes(
'This is just a primary content placeholder.',
),
).toBe(true)
expect(IndexWrapper.html().includes('About Leaderboards.gg')).toBe(true)
})
})
9 changes: 4 additions & 5 deletions pages/index.vue
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
<script setup lang="ts"></script>
<script setup lang="ts">
import Landing from '~/components/blocks/Landing/Landing.vue'
</script>

<template>
<div class="flex grow justify-center p-5">
<div class="w-full rounded border border-gray-300 p-5">
<h2 class="text-center text-2xl font-bold">
{{ $t(`welcome`) }}
</h2>
<p class="text-center">This is just a primary content placeholder.</p>
<Landing />
</div>
</div>
</template>