@userfrosting/sprinkle-core 6.0.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +7 -0
- package/README.md +89 -0
- package/app/assets/composables/index.ts +1 -0
- package/app/assets/composables/sprunjer.ts +164 -0
- package/app/assets/index.ts +8 -0
- package/app/assets/interfaces/alerts.ts +16 -0
- package/app/assets/interfaces/common.ts +8 -0
- package/app/assets/interfaces/index.ts +4 -0
- package/app/assets/interfaces/severity.ts +24 -0
- package/app/assets/interfaces/sprunjer.ts +26 -0
- package/app/assets/stores/config.ts +30 -0
- package/app/assets/stores/index.ts +1 -0
- package/app/assets/tests/interfaces/alerts.test.ts +43 -0
- package/app/assets/tests/plugin.test.ts +19 -0
- package/app/assets/tests/stores/config.test.ts +42 -0
- package/package.json +76 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Copyright (c) 2013-2024 Alexander Weissman & Louis Charette
|
|
2
|
+
|
|
3
|
+
UserFrosting is 100% free and open-source.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# UserFrosting 5.2 Core Sprinkle
|
|
2
|
+
|
|
3
|
+
[](https://github.com/userfrosting/sprinkle-core/releases)
|
|
4
|
+

|
|
5
|
+
[](LICENSE.md)
|
|
6
|
+
[](https://github.com/userfrosting/sprinkle-core/actions)
|
|
7
|
+
[](https://app.codecov.io/gh/userfrosting/sprinkle-core/branch/5.2)
|
|
8
|
+
[](https://github.styleci.io/repos/372359383)
|
|
9
|
+
[](https://github.com/userfrosting/sprinkle-core/actions/workflows/PHPStan.yml)
|
|
10
|
+
[](https://chat.userfrosting.com)
|
|
11
|
+
[](https://opencollective.com/userfrosting#backer)
|
|
12
|
+
[](https://ko-fi.com/lcharette)
|
|
13
|
+
|
|
14
|
+
## By [Alex Weissman](https://alexanderweissman.com) and [Louis Charette](https://bbqsoftwares.com)
|
|
15
|
+
|
|
16
|
+
Copyright (c) 2013-2024, free to use in personal and commercial software as per the [license](LICENSE.md).
|
|
17
|
+
|
|
18
|
+
UserFrosting is a secure, modern user management system written in PHP and built on top of the [Slim Microframework](http://www.slimframework.com/), [Twig](http://twig.sensiolabs.org/) templating engine, and [Eloquent](https://laravel.com/docs/10.x/eloquent#introduction) ORM.
|
|
19
|
+
|
|
20
|
+
This **Core Sprinkle** provides most of the "heavy lifting" PHP code. It provides all the necessary services for database, templating, error handling, mail support, request throttling, and more.
|
|
21
|
+
|
|
22
|
+
## Installation in your UserFrosting project
|
|
23
|
+
To use this sprinkle in your UserFrosting project, follow theses instructions (*N.B.: This sprinkle is enabled by default when using the base app template*).
|
|
24
|
+
|
|
25
|
+
1. Require in your [UserFrosting](https://github.com/userfrosting/UserFrosting) project :
|
|
26
|
+
```
|
|
27
|
+
composer require userfrosting/sprinkle-core
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
2. Add the Sprinkle to your Sprinkle Recipe :
|
|
31
|
+
```php
|
|
32
|
+
public function getSprinkles(): array
|
|
33
|
+
{
|
|
34
|
+
return [
|
|
35
|
+
\UserFrosting\Sprinkle\Core\Core::class,
|
|
36
|
+
];
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
3. Bake
|
|
41
|
+
```bash
|
|
42
|
+
php bakery bake
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Install locally and run tests
|
|
46
|
+
You can also install this sprinkle locally. This can be useful to debug or contribute to this sprinkle.
|
|
47
|
+
|
|
48
|
+
1. Clone repo :
|
|
49
|
+
```
|
|
50
|
+
git clone https://github.com/userfrosting/sprinkle-core.git
|
|
51
|
+
```
|
|
52
|
+
2. Change directory
|
|
53
|
+
```
|
|
54
|
+
cd sprinkle-core
|
|
55
|
+
```
|
|
56
|
+
3. Install dependencies :
|
|
57
|
+
```
|
|
58
|
+
composer install
|
|
59
|
+
```
|
|
60
|
+
4. Run bake command :
|
|
61
|
+
```
|
|
62
|
+
php bakery bake
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
From this point, you can use the same command as with any other sprinkle.
|
|
66
|
+
|
|
67
|
+
Tests can be run using the bundled PHPUnit :
|
|
68
|
+
```
|
|
69
|
+
vendor/bin/phpunit
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Same for PHPStan, for code quality :
|
|
73
|
+
```
|
|
74
|
+
vendor/bin/phpstan analyse app/src/
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Documentation
|
|
78
|
+
See main [UserFrosting Documentation](https://learn.userfrosting.com) for more information.
|
|
79
|
+
|
|
80
|
+
- [Changelog](CHANGELOG.md)
|
|
81
|
+
- [Issues](https://github.com/userfrosting/UserFrosting/issues)
|
|
82
|
+
- [License](LICENSE.md)
|
|
83
|
+
- [Style Guide](https://github.com/userfrosting/.github/blob/main/.github/STYLE-GUIDE.md)
|
|
84
|
+
|
|
85
|
+
## Contributing
|
|
86
|
+
|
|
87
|
+
This project exists thanks to all the people who contribute. If you're interested in contributing to the UserFrosting codebase, please see our [contributing guidelines](https://github.com/userfrosting/UserFrosting/blob/5.2/.github/CONTRIBUTING.md) as well as our [style guidelines](.github/STYLE-GUIDE.md).
|
|
88
|
+
|
|
89
|
+
[](https://github.com/userfrosting/sprinkle-core/graphs/contributors)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { useSprunjer } from './sprunjer'
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sprunjer Composable
|
|
3
|
+
*
|
|
4
|
+
* A composable function that fetches data from a Sprunjer API and provides
|
|
5
|
+
* all necessary function like pagination, sorting and filtering.
|
|
6
|
+
*
|
|
7
|
+
* Pass the URL of the Sprunjer API to the function, and it will fetch the data.
|
|
8
|
+
* A watcher will refetch the data whenever any parameters change.
|
|
9
|
+
*
|
|
10
|
+
* Params:
|
|
11
|
+
* @param {String} dataUrl - The URL of the Sprunjer API
|
|
12
|
+
* @param {Object} defaultSorts - An object of default sorts
|
|
13
|
+
* @param {Object} defaultFilters - An object of default filters
|
|
14
|
+
* @param {Number} defaultSize - The default number of items per page
|
|
15
|
+
* @param {Number} defaultPage - The default page number
|
|
16
|
+
*
|
|
17
|
+
* Exports:
|
|
18
|
+
* - size: The number of items per page
|
|
19
|
+
* - page: The current page number
|
|
20
|
+
* - sorts: An object of sorts
|
|
21
|
+
* - filters: An object of filters
|
|
22
|
+
* - data: The raw data from the API
|
|
23
|
+
* - fetch: A function to fetch the data
|
|
24
|
+
* - loading: A boolean indicating if the data is loading
|
|
25
|
+
* - totalPages: The total number of pages
|
|
26
|
+
* - downloadCsv: A function to download the data as a CSV file
|
|
27
|
+
* - countFiltered: The total number of items after filtering
|
|
28
|
+
* - count: The total number of items
|
|
29
|
+
* - rows: The rows of data
|
|
30
|
+
* - first: The index of the first item on the current page
|
|
31
|
+
* - last: The index of the last item on the current page
|
|
32
|
+
* - toggleSort: A function to toggle the sort order of a column
|
|
33
|
+
*/
|
|
34
|
+
import { ref, toValue, watchEffect, computed } from 'vue'
|
|
35
|
+
import axios from 'axios'
|
|
36
|
+
import type { AssociativeArray, Sprunjer } from '../interfaces'
|
|
37
|
+
|
|
38
|
+
export const useSprunjer = (
|
|
39
|
+
dataUrl: any,
|
|
40
|
+
defaultSorts: AssociativeArray = {},
|
|
41
|
+
defaultFilters: AssociativeArray = {},
|
|
42
|
+
defaultSize: number = 10,
|
|
43
|
+
defaultPage: number = 0
|
|
44
|
+
): Sprunjer => {
|
|
45
|
+
// Sprunje parameters
|
|
46
|
+
const size = ref<number>(defaultSize)
|
|
47
|
+
const page = ref<number>(defaultPage)
|
|
48
|
+
const sorts = ref<AssociativeArray>(defaultSorts)
|
|
49
|
+
const filters = ref<AssociativeArray>(defaultFilters)
|
|
50
|
+
|
|
51
|
+
// Raw data
|
|
52
|
+
const data = ref<any>({})
|
|
53
|
+
|
|
54
|
+
// State
|
|
55
|
+
const loading = ref<boolean>(false)
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Api fetch function
|
|
59
|
+
*/
|
|
60
|
+
async function fetch() {
|
|
61
|
+
loading.value = true
|
|
62
|
+
axios
|
|
63
|
+
.get(toValue(dataUrl), {
|
|
64
|
+
params: {
|
|
65
|
+
size: size.value,
|
|
66
|
+
page: page.value,
|
|
67
|
+
sorts: sorts.value,
|
|
68
|
+
filters: filters.value
|
|
69
|
+
}
|
|
70
|
+
})
|
|
71
|
+
.then((response) => {
|
|
72
|
+
data.value = response.data
|
|
73
|
+
loading.value = false
|
|
74
|
+
})
|
|
75
|
+
.catch((err) => {
|
|
76
|
+
// TODO : User toast alert, or export alert
|
|
77
|
+
console.error(err)
|
|
78
|
+
})
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Computed properties
|
|
83
|
+
*/
|
|
84
|
+
const totalPages = computed(() => {
|
|
85
|
+
// N.B.: Sprunjer page starts at 0, not 1
|
|
86
|
+
// Make sure page is never negative
|
|
87
|
+
return Math.max(Math.ceil((data.value.count_filtered ?? 0) / size.value) - 1, 0)
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
const count = computed(() => {
|
|
91
|
+
return data.value.count ?? 0
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
const first = computed(() => {
|
|
95
|
+
return Math.min(page.value * size.value + 1, data.value.count ?? 0)
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
const last = computed(() => {
|
|
99
|
+
return Math.min((page.value + 1) * size.value, data.value.count_filtered ?? 0)
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
const countFiltered = computed(() => {
|
|
103
|
+
return data.value.count_filtered ?? 0
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
const rows = computed(() => {
|
|
107
|
+
return data.value.rows ?? []
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Download the data as a CSV file
|
|
112
|
+
*/
|
|
113
|
+
function downloadCsv() {
|
|
114
|
+
console.log('Not yet implemented')
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Apply sorting to a column, cycling from the previous sort order.
|
|
119
|
+
* Order goes : asc -> desc -> null -> asc
|
|
120
|
+
* Used to toggle the sort order of a column when the column header is clicked
|
|
121
|
+
* @param column The column to sort
|
|
122
|
+
*/
|
|
123
|
+
function toggleSort(column: string) {
|
|
124
|
+
let newOrder: string | null
|
|
125
|
+
if (sorts.value[column] === 'asc') {
|
|
126
|
+
newOrder = 'desc'
|
|
127
|
+
} else if (sorts.value[column] === 'desc') {
|
|
128
|
+
newOrder = null
|
|
129
|
+
} else {
|
|
130
|
+
newOrder = 'asc'
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
sorts.value[column] = newOrder
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Automatically fetch the data when any parameters change
|
|
138
|
+
*/
|
|
139
|
+
watchEffect(() => {
|
|
140
|
+
fetch()
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Export the functions and data
|
|
145
|
+
*/
|
|
146
|
+
return {
|
|
147
|
+
dataUrl,
|
|
148
|
+
size,
|
|
149
|
+
page,
|
|
150
|
+
sorts,
|
|
151
|
+
filters,
|
|
152
|
+
data,
|
|
153
|
+
fetch,
|
|
154
|
+
loading,
|
|
155
|
+
downloadCsv,
|
|
156
|
+
totalPages,
|
|
157
|
+
countFiltered,
|
|
158
|
+
count,
|
|
159
|
+
rows,
|
|
160
|
+
first,
|
|
161
|
+
last,
|
|
162
|
+
toggleSort
|
|
163
|
+
}
|
|
164
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Alert Interface
|
|
3
|
+
*
|
|
4
|
+
* Represents a common interface for alert components. This interface is used by
|
|
5
|
+
* API when an error occurs or a successful event occurs, and consumed by the
|
|
6
|
+
* interface.
|
|
7
|
+
*/
|
|
8
|
+
import { Severity } from './severity'
|
|
9
|
+
|
|
10
|
+
export interface AlertInterface {
|
|
11
|
+
title?: string
|
|
12
|
+
description?: string
|
|
13
|
+
style?: Severity | keyof typeof Severity
|
|
14
|
+
closeBtn?: boolean
|
|
15
|
+
hideIcon?: boolean
|
|
16
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Severity Enum
|
|
3
|
+
*
|
|
4
|
+
* This is a shared enum used to define the severity of different components
|
|
5
|
+
* (e.g., alert, button, etc.). This makes it easier to reference severity
|
|
6
|
+
* levels across multiple components, also defining a common concept across
|
|
7
|
+
* themes.
|
|
8
|
+
*
|
|
9
|
+
* Template components must accept all values of this enum as valid input.
|
|
10
|
+
* However, themes may choose to ignore or bind some values to another style.
|
|
11
|
+
* For example, a theme might not have an 'Info' colored button. In this case,
|
|
12
|
+
* the theme's button component must accept 'Info' as a valid input, but it can
|
|
13
|
+
* map it to the 'Primary' style.
|
|
14
|
+
*/
|
|
15
|
+
export enum Severity {
|
|
16
|
+
Primary = 'Primary',
|
|
17
|
+
Secondary = 'Secondary',
|
|
18
|
+
Success = 'Success',
|
|
19
|
+
Warning = 'Warning',
|
|
20
|
+
Danger = 'Danger',
|
|
21
|
+
Info = 'Info',
|
|
22
|
+
Muted = 'Muted', // Aka, Disabled
|
|
23
|
+
Default = 'Default' // No-style or default style
|
|
24
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sprunjer Interface
|
|
3
|
+
*
|
|
4
|
+
* Represents the interface for the Sprunjer composable.
|
|
5
|
+
*/
|
|
6
|
+
import type { Ref, ComputedRef } from 'vue'
|
|
7
|
+
import type { AssociativeArray } from '.'
|
|
8
|
+
|
|
9
|
+
export interface Sprunjer {
|
|
10
|
+
dataUrl: any
|
|
11
|
+
size: Ref<number>
|
|
12
|
+
page: Ref<number>
|
|
13
|
+
sorts: Ref<AssociativeArray>
|
|
14
|
+
filters: Ref<AssociativeArray>
|
|
15
|
+
data: Ref<any>
|
|
16
|
+
fetch: () => void
|
|
17
|
+
loading: Ref<boolean>
|
|
18
|
+
totalPages: ComputedRef<number>
|
|
19
|
+
downloadCsv: () => void
|
|
20
|
+
countFiltered: ComputedRef<number>
|
|
21
|
+
count: ComputedRef<number>
|
|
22
|
+
rows: ComputedRef<any[]>
|
|
23
|
+
first: ComputedRef<number>
|
|
24
|
+
last: ComputedRef<number>
|
|
25
|
+
toggleSort: (column: string) => void
|
|
26
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Config Store
|
|
3
|
+
*
|
|
4
|
+
* This store is used to access the configuration of the application from the
|
|
5
|
+
* API.
|
|
6
|
+
*/
|
|
7
|
+
import { defineStore } from 'pinia'
|
|
8
|
+
import axios from 'axios'
|
|
9
|
+
import { getProperty } from 'dot-prop'
|
|
10
|
+
|
|
11
|
+
export const useConfigStore = defineStore('config', {
|
|
12
|
+
persist: true,
|
|
13
|
+
state: () => {
|
|
14
|
+
return {
|
|
15
|
+
config: {}
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
getters: {
|
|
19
|
+
get: (state) => {
|
|
20
|
+
return (key: string, value?: any): any => getProperty(state.config, key, value)
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
actions: {
|
|
24
|
+
async load() {
|
|
25
|
+
axios.get('/api/config').then((response) => {
|
|
26
|
+
this.config = response.data
|
|
27
|
+
})
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
})
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { useConfigStore } from './config'
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { describe, expect, test } from 'vitest'
|
|
2
|
+
import { type AlertInterface, Severity } from '../../interfaces'
|
|
3
|
+
|
|
4
|
+
describe('AlertInterface', () => {
|
|
5
|
+
test('should create an alert with title and description', () => {
|
|
6
|
+
const alert: AlertInterface = {
|
|
7
|
+
title: 'Test Alert',
|
|
8
|
+
description: 'This is a test alert'
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
expect(alert.title).toBe('Test Alert')
|
|
12
|
+
expect(alert.description).toBe('This is a test alert')
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
test('should create an alert with style', () => {
|
|
16
|
+
const alert: AlertInterface = {
|
|
17
|
+
style: Severity.Success
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
expect(alert.style).toBe(Severity.Success)
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
test('should create an alert with close button and hide icon', () => {
|
|
24
|
+
const alert: AlertInterface = {
|
|
25
|
+
closeBtn: true,
|
|
26
|
+
hideIcon: true
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
expect(alert.closeBtn).toBe(true)
|
|
30
|
+
expect(alert.hideIcon).toBe(true)
|
|
31
|
+
})
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
describe('Severity', () => {
|
|
35
|
+
test('should have the correct values', () => {
|
|
36
|
+
expect(Severity.Primary).toBe('Primary')
|
|
37
|
+
expect(Severity.Secondary).toBe('Secondary')
|
|
38
|
+
expect(Severity.Success).toBe('Success')
|
|
39
|
+
expect(Severity.Warning).toBe('Warning')
|
|
40
|
+
expect(Severity.Danger).toBe('Danger')
|
|
41
|
+
expect(Severity.Info).toBe('Info')
|
|
42
|
+
})
|
|
43
|
+
})
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { describe, expect, test, vi } from 'vitest'
|
|
2
|
+
import { useConfigStore } from '../stores/config'
|
|
3
|
+
import plugin from '..'
|
|
4
|
+
import * as Config from '../stores/config'
|
|
5
|
+
|
|
6
|
+
const mockConfigStore = {
|
|
7
|
+
load: vi.fn()
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
describe('Plugin', () => {
|
|
11
|
+
test('should install the plugin and initiate load', () => {
|
|
12
|
+
vi.spyOn(Config, 'useConfigStore').mockReturnValue(mockConfigStore as any)
|
|
13
|
+
|
|
14
|
+
plugin.install()
|
|
15
|
+
|
|
16
|
+
expect(useConfigStore).toHaveBeenCalled()
|
|
17
|
+
expect(mockConfigStore.load).toHaveBeenCalled()
|
|
18
|
+
})
|
|
19
|
+
})
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { describe, expect, beforeEach, test, vi } from 'vitest'
|
|
2
|
+
import { setActivePinia, createPinia } from 'pinia'
|
|
3
|
+
import axios from 'axios'
|
|
4
|
+
import { useConfigStore } from '../../stores/config'
|
|
5
|
+
|
|
6
|
+
const testConfig = {
|
|
7
|
+
name: 'Test Config',
|
|
8
|
+
description: 'Test description',
|
|
9
|
+
api: {
|
|
10
|
+
url: 'https://api.example.com',
|
|
11
|
+
version: '1.0'
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
describe('Config Store', () => {
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
setActivePinia(createPinia())
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
test('should load config data', async () => {
|
|
21
|
+
// Arrange
|
|
22
|
+
const configStore = useConfigStore()
|
|
23
|
+
const response = { data: testConfig }
|
|
24
|
+
vi.spyOn(axios, 'get').mockResolvedValue(response as any)
|
|
25
|
+
|
|
26
|
+
// Assert initial state
|
|
27
|
+
expect(configStore.config).toEqual({})
|
|
28
|
+
|
|
29
|
+
// Act
|
|
30
|
+
await configStore.load()
|
|
31
|
+
|
|
32
|
+
// Assert
|
|
33
|
+
expect(axios.get).toHaveBeenCalledWith('/api/config')
|
|
34
|
+
expect(configStore.config).toStrictEqual(testConfig)
|
|
35
|
+
|
|
36
|
+
// Assert get method
|
|
37
|
+
expect(configStore.get('name')).toBe('Test Config')
|
|
38
|
+
expect(configStore.get('api.url')).toBe('https://api.example.com')
|
|
39
|
+
expect(configStore.get('api.version', '0.0')).toBe('1.0')
|
|
40
|
+
expect(configStore.get('api.key', 'API_KEY')).toBe('API_KEY')
|
|
41
|
+
})
|
|
42
|
+
})
|
package/package.json
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@userfrosting/sprinkle-core",
|
|
3
|
+
"version": "6.0.0-alpha.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Core Sprinkle for UserFrosting",
|
|
6
|
+
"funding": "https://opencollective.com/userfrosting",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"author": "Louis Charette (https://bbqsoftwares.com/)",
|
|
9
|
+
"contributors": [
|
|
10
|
+
"Alexander Weissman (https://alexanderweissman.com/)",
|
|
11
|
+
"Louis Charette (https://bbqsoftwares.com/)"
|
|
12
|
+
],
|
|
13
|
+
"keywords": [
|
|
14
|
+
"UserFrosting",
|
|
15
|
+
"Core",
|
|
16
|
+
"Sprinkle"
|
|
17
|
+
],
|
|
18
|
+
"homepage": "https://github.com/userfrosting/sprinkle-core#readme",
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://github.com/userfrosting/sprinkle-core.git"
|
|
22
|
+
},
|
|
23
|
+
"bugs": {
|
|
24
|
+
"url": "https://github.com/userfrosting/UserFrosting/issues"
|
|
25
|
+
},
|
|
26
|
+
"exports": {
|
|
27
|
+
".": "./app/assets/index.ts",
|
|
28
|
+
"./interfaces": "./app/assets/interfaces/index.ts",
|
|
29
|
+
"./stores": "./app/assets/stores/index.ts",
|
|
30
|
+
"./composables": "./app/assets/composables/index.ts"
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"app/assets/"
|
|
34
|
+
],
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"dot-prop": "^9.0.0"
|
|
37
|
+
},
|
|
38
|
+
"peerDependencies": {
|
|
39
|
+
"axios": "^1.5.0",
|
|
40
|
+
"pinia": "^2.1.6",
|
|
41
|
+
"pinia-plugin-persistedstate": "^3.2.0",
|
|
42
|
+
"vue": "^3.4.21"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@rushstack/eslint-patch": "^1.8.0",
|
|
46
|
+
"@tsconfig/node20": "^20.1.4",
|
|
47
|
+
"@types/node": "^20.12.5",
|
|
48
|
+
"@vitejs/plugin-vue": "^5.0.4",
|
|
49
|
+
"@vitest/coverage-v8": "^1.6.0",
|
|
50
|
+
"@vue/eslint-config-prettier": "^9.0.0",
|
|
51
|
+
"@vue/eslint-config-typescript": "^13.0.0",
|
|
52
|
+
"@vue/test-utils": "^2.4.6",
|
|
53
|
+
"@vue/tsconfig": "^0.5.1",
|
|
54
|
+
"eslint": "^8.57.0",
|
|
55
|
+
"eslint-plugin-vue": "^9.23.0",
|
|
56
|
+
"happy-dom": "^15.11.6",
|
|
57
|
+
"less": "^4.2.0",
|
|
58
|
+
"npm-run-all2": "^6.1.2",
|
|
59
|
+
"prettier": "^3.2.5",
|
|
60
|
+
"vite": "^5.2.8",
|
|
61
|
+
"vite-plugin-dts": "^4.0.0",
|
|
62
|
+
"vitest": "^1.6.0",
|
|
63
|
+
"vue": "^3.4.21",
|
|
64
|
+
"vue-router": "^4.2.4",
|
|
65
|
+
"vue-tsc": "^2.0.11"
|
|
66
|
+
},
|
|
67
|
+
"scripts": {
|
|
68
|
+
"dev": "vite",
|
|
69
|
+
"typecheck": "vue-tsc --noEmit",
|
|
70
|
+
"build": "vue-tsc && vite build",
|
|
71
|
+
"lint": "eslint app/assets/ --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
|
|
72
|
+
"format": "prettier --write app/assets/",
|
|
73
|
+
"test": "vitest",
|
|
74
|
+
"coverage": "vitest run --coverage"
|
|
75
|
+
}
|
|
76
|
+
}
|