From 37da4f44ba6263e3452c827b56921cf5125e0afa Mon Sep 17 00:00:00 2001 From: Mia Mastoridis Date: Tue, 26 Mar 2024 15:45:10 -0400 Subject: [PATCH 1/2] post method --- internal/client/api_statuses.go | 320 +++++++++++++++++++++ internal/client/model_models_add_status.go | 268 +++++++++++++++++ 2 files changed, 588 insertions(+) create mode 100644 internal/client/api_statuses.go create mode 100644 internal/client/model_models_add_status.go diff --git a/internal/client/api_statuses.go b/internal/client/api_statuses.go new file mode 100644 index 000000000..d11929cab --- /dev/null +++ b/internal/client/api_statuses.go @@ -0,0 +1,320 @@ +/* +Nexodus API + +This is the Nexodus API Server. + +API version: 1.0 +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package client + +import ( + "bytes" + "context" + "io" + "net/http" + "net/url" +) + +// StatusesApiService StatusesApi service +type StatusesApiService service + +type ApiCreateStatusRequest struct { + ctx context.Context + ApiService *StatusesApiService + status *ModelsAddStatus +} + +// Add Status +func (r ApiCreateStatusRequest) Status(status ModelsAddStatus) ApiCreateStatusRequest { + r.status = &status + return r +} + +func (r ApiCreateStatusRequest) Execute() (*ModelsStatus, *http.Response, error) { + return r.ApiService.CreateStatusExecute(r) +} + +/* +CreateStatus Add Statuses + +Adds a new status + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @return ApiCreateStatusRequest +*/ +func (a *StatusesApiService) CreateStatus(ctx context.Context) ApiCreateStatusRequest { + return ApiCreateStatusRequest{ + ApiService: a, + ctx: ctx, + } +} + +// Execute executes the request +// +// @return ModelsStatus +func (a *StatusesApiService) CreateStatusExecute(r ApiCreateStatusRequest) (*ModelsStatus, *http.Response, error) { + var ( + localVarHTTPMethod = http.MethodPost + localVarPostBody interface{} + formFiles []formFile + localVarReturnValue *ModelsStatus + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "StatusesApiService.CreateStatus") + if err != nil { + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/api/status" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + if r.status == nil { + return localVarReturnValue, nil, reportError("status is required and must be specified") + } + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{"application/json"} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + // body params + localVarPostBody = r.status + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + if localVarHTTPResponse.StatusCode == 400 { + var v ModelsBaseError + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 401 { + var v ModelsBaseError + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 409 { + var v ModelsConflictsError + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 429 { + var v ModelsBaseError + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 500 { + var v ModelsInternalServerError + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) + newErr.model = v + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + +type ApiListStatusesRequest struct { + ctx context.Context + ApiService *StatusesApiService +} + +func (r ApiListStatusesRequest) Execute() ([]ModelsStatus, *http.Response, error) { + return r.ApiService.ListStatusesExecute(r) +} + +/* +ListStatuses List Statuses + +Lists all Statuses + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @return ApiListStatusesRequest +*/ +func (a *StatusesApiService) ListStatuses(ctx context.Context) ApiListStatusesRequest { + return ApiListStatusesRequest{ + ApiService: a, + ctx: ctx, + } +} + +// Execute executes the request +// +// @return []ModelsStatus +func (a *StatusesApiService) ListStatusesExecute(r ApiListStatusesRequest) ([]ModelsStatus, *http.Response, error) { + var ( + localVarHTTPMethod = http.MethodGet + localVarPostBody interface{} + formFiles []formFile + localVarReturnValue []ModelsStatus + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "StatusesApiService.ListStatuses") + if err != nil { + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/api/statues" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + if localVarHTTPResponse.StatusCode == 401 { + var v ModelsBaseError + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 429 { + var v ModelsBaseError + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 500 { + var v ModelsInternalServerError + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) + newErr.model = v + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} diff --git a/internal/client/model_models_add_status.go b/internal/client/model_models_add_status.go new file mode 100644 index 000000000..e29854cb0 --- /dev/null +++ b/internal/client/model_models_add_status.go @@ -0,0 +1,268 @@ +/* +Nexodus API + +This is the Nexodus API Server. + +API version: 1.0 +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package client + +import ( + "encoding/json" +) + +// checks if the ModelsAddStatus type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &ModelsAddStatus{} + +// ModelsAddStatus struct for ModelsAddStatus +type ModelsAddStatus struct { + Hostname *string `json:"hostname,omitempty"` + IsReachable *bool `json:"is_reachable,omitempty"` + Latency *string `json:"latency,omitempty"` + Method *string `json:"method,omitempty"` + WgIp *string `json:"wg_ip,omitempty"` +} + +// NewModelsAddStatus instantiates a new ModelsAddStatus object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewModelsAddStatus() *ModelsAddStatus { + this := ModelsAddStatus{} + return &this +} + +// NewModelsAddStatusWithDefaults instantiates a new ModelsAddStatus object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewModelsAddStatusWithDefaults() *ModelsAddStatus { + this := ModelsAddStatus{} + return &this +} + +// GetHostname returns the Hostname field value if set, zero value otherwise. +func (o *ModelsAddStatus) GetHostname() string { + if o == nil || IsNil(o.Hostname) { + var ret string + return ret + } + return *o.Hostname +} + +// GetHostnameOk returns a tuple with the Hostname field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *ModelsAddStatus) GetHostnameOk() (*string, bool) { + if o == nil || IsNil(o.Hostname) { + return nil, false + } + return o.Hostname, true +} + +// HasHostname returns a boolean if a field has been set. +func (o *ModelsAddStatus) HasHostname() bool { + if o != nil && !IsNil(o.Hostname) { + return true + } + + return false +} + +// SetHostname gets a reference to the given string and assigns it to the Hostname field. +func (o *ModelsAddStatus) SetHostname(v string) { + o.Hostname = &v +} + +// GetIsReachable returns the IsReachable field value if set, zero value otherwise. +func (o *ModelsAddStatus) GetIsReachable() bool { + if o == nil || IsNil(o.IsReachable) { + var ret bool + return ret + } + return *o.IsReachable +} + +// GetIsReachableOk returns a tuple with the IsReachable field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *ModelsAddStatus) GetIsReachableOk() (*bool, bool) { + if o == nil || IsNil(o.IsReachable) { + return nil, false + } + return o.IsReachable, true +} + +// HasIsReachable returns a boolean if a field has been set. +func (o *ModelsAddStatus) HasIsReachable() bool { + if o != nil && !IsNil(o.IsReachable) { + return true + } + + return false +} + +// SetIsReachable gets a reference to the given bool and assigns it to the IsReachable field. +func (o *ModelsAddStatus) SetIsReachable(v bool) { + o.IsReachable = &v +} + +// GetLatency returns the Latency field value if set, zero value otherwise. +func (o *ModelsAddStatus) GetLatency() string { + if o == nil || IsNil(o.Latency) { + var ret string + return ret + } + return *o.Latency +} + +// GetLatencyOk returns a tuple with the Latency field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *ModelsAddStatus) GetLatencyOk() (*string, bool) { + if o == nil || IsNil(o.Latency) { + return nil, false + } + return o.Latency, true +} + +// HasLatency returns a boolean if a field has been set. +func (o *ModelsAddStatus) HasLatency() bool { + if o != nil && !IsNil(o.Latency) { + return true + } + + return false +} + +// SetLatency gets a reference to the given string and assigns it to the Latency field. +func (o *ModelsAddStatus) SetLatency(v string) { + o.Latency = &v +} + +// GetMethod returns the Method field value if set, zero value otherwise. +func (o *ModelsAddStatus) GetMethod() string { + if o == nil || IsNil(o.Method) { + var ret string + return ret + } + return *o.Method +} + +// GetMethodOk returns a tuple with the Method field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *ModelsAddStatus) GetMethodOk() (*string, bool) { + if o == nil || IsNil(o.Method) { + return nil, false + } + return o.Method, true +} + +// HasMethod returns a boolean if a field has been set. +func (o *ModelsAddStatus) HasMethod() bool { + if o != nil && !IsNil(o.Method) { + return true + } + + return false +} + +// SetMethod gets a reference to the given string and assigns it to the Method field. +func (o *ModelsAddStatus) SetMethod(v string) { + o.Method = &v +} + +// GetWgIp returns the WgIp field value if set, zero value otherwise. +func (o *ModelsAddStatus) GetWgIp() string { + if o == nil || IsNil(o.WgIp) { + var ret string + return ret + } + return *o.WgIp +} + +// GetWgIpOk returns a tuple with the WgIp field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *ModelsAddStatus) GetWgIpOk() (*string, bool) { + if o == nil || IsNil(o.WgIp) { + return nil, false + } + return o.WgIp, true +} + +// HasWgIp returns a boolean if a field has been set. +func (o *ModelsAddStatus) HasWgIp() bool { + if o != nil && !IsNil(o.WgIp) { + return true + } + + return false +} + +// SetWgIp gets a reference to the given string and assigns it to the WgIp field. +func (o *ModelsAddStatus) SetWgIp(v string) { + o.WgIp = &v +} + +func (o ModelsAddStatus) MarshalJSON() ([]byte, error) { + toSerialize, err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o ModelsAddStatus) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + if !IsNil(o.Hostname) { + toSerialize["hostname"] = o.Hostname + } + if !IsNil(o.IsReachable) { + toSerialize["is_reachable"] = o.IsReachable + } + if !IsNil(o.Latency) { + toSerialize["latency"] = o.Latency + } + if !IsNil(o.Method) { + toSerialize["method"] = o.Method + } + if !IsNil(o.WgIp) { + toSerialize["wg_ip"] = o.WgIp + } + return toSerialize, nil +} + +type NullableModelsAddStatus struct { + value *ModelsAddStatus + isSet bool +} + +func (v NullableModelsAddStatus) Get() *ModelsAddStatus { + return v.value +} + +func (v *NullableModelsAddStatus) Set(val *ModelsAddStatus) { + v.value = val + v.isSet = true +} + +func (v NullableModelsAddStatus) IsSet() bool { + return v.isSet +} + +func (v *NullableModelsAddStatus) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableModelsAddStatus(val *ModelsAddStatus) *NullableModelsAddStatus { + return &NullableModelsAddStatus{value: val, isSet: true} +} + +func (v NullableModelsAddStatus) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableModelsAddStatus) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} From 28ca89bfe14439be518435f98d34cdae773eb0a2 Mon Sep 17 00:00:00 2001 From: Mia Mastoridis Date: Tue, 26 Mar 2024 15:47:17 -0400 Subject: [PATCH 2/2] post methods post methods --- internal/client/.openapi-generator/FILES | 2 + internal/client/api_status.go | 298 ----------------- internal/client/client.go | 3 + internal/docs/docs.go | 36 ++- internal/docs/swagger.json | 36 ++- internal/docs/swagger.yaml | 29 +- internal/handlers/status.go | 36 +-- internal/models/status.go | 8 + internal/nexodus/ctlconnectivty.go | 59 ++-- ui/index.html | 93 +++++- ui/package-lock.json | 2 +- ui/playwright.config.ts | 20 +- ui/src/App.tsx | 4 +- ui/src/pages/Dashboard.tsx | 322 ++++++++++++------- ui/src/setupTests (copy).ts | 2 - ui/src/setupTests.ts | 2 +- ui/tests-examples/demo-todo-app.spec.ts | 388 +++++++++++++---------- ui/tests-examples/example.spec.ts | 16 +- ui/tests/env.ts | 6 +- ui/tests/login.spec.ts | 120 +++---- 20 files changed, 727 insertions(+), 755 deletions(-) delete mode 100644 ui/src/setupTests (copy).ts diff --git a/internal/client/.openapi-generator/FILES b/internal/client/.openapi-generator/FILES index d15e0fdd5..6bbaf68ee 100644 --- a/internal/client/.openapi-generator/FILES +++ b/internal/client/.openapi-generator/FILES @@ -10,6 +10,7 @@ api_security_group.go api_service_network.go api_sites.go api_status.go +api_statuses.go api_users.go api_vpc.go client.go @@ -21,6 +22,7 @@ model_models_add_reg_key.go model_models_add_security_group.go model_models_add_service_network.go model_models_add_site.go +model_models_add_status.go model_models_add_vpc.go model_models_base_error.go model_models_certificate_signing_request.go diff --git a/internal/client/api_status.go b/internal/client/api_status.go index 1da6cd2b5..fb120cc82 100644 --- a/internal/client/api_status.go +++ b/internal/client/api_status.go @@ -22,304 +22,6 @@ import ( // StatusApiService StatusApi service type StatusApiService service -type ApiCreateStatusRequest struct { - ctx context.Context - ApiService *StatusApiService - status *ModelsStatus -} - -// Status -func (r ApiCreateStatusRequest) Status(status ModelsStatus) ApiCreateStatusRequest { - r.status = &status - return r -} - -func (r ApiCreateStatusRequest) Execute() (*ModelsStatus, *http.Response, error) { - return r.ApiService.CreateStatusExecute(r) -} - -/* -CreateStatus Add Status - -Adds a new status - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @return ApiCreateStatusRequest -*/ -func (a *StatusApiService) CreateStatus(ctx context.Context) ApiCreateStatusRequest { - return ApiCreateStatusRequest{ - ApiService: a, - ctx: ctx, - } -} - -// Execute executes the request -// -// @return ModelsStatus -func (a *StatusApiService) CreateStatusExecute(r ApiCreateStatusRequest) (*ModelsStatus, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *ModelsStatus - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "StatusApiService.CreateStatus") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/api/status" - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.status == nil { - return localVarReturnValue, nil, reportError("status is required and must be specified") - } - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{"application/json"} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - // body params - localVarPostBody = r.status - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - if localVarHTTPResponse.StatusCode == 400 { - var v ModelsBaseError - err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr.error = err.Error() - return localVarReturnValue, localVarHTTPResponse, newErr - } - newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) - newErr.model = v - return localVarReturnValue, localVarHTTPResponse, newErr - } - if localVarHTTPResponse.StatusCode == 401 { - var v ModelsBaseError - err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr.error = err.Error() - return localVarReturnValue, localVarHTTPResponse, newErr - } - newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) - newErr.model = v - return localVarReturnValue, localVarHTTPResponse, newErr - } - if localVarHTTPResponse.StatusCode == 409 { - var v ModelsConflictsError - err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr.error = err.Error() - return localVarReturnValue, localVarHTTPResponse, newErr - } - newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) - newErr.model = v - return localVarReturnValue, localVarHTTPResponse, newErr - } - if localVarHTTPResponse.StatusCode == 429 { - var v ModelsBaseError - err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr.error = err.Error() - return localVarReturnValue, localVarHTTPResponse, newErr - } - newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) - newErr.model = v - return localVarReturnValue, localVarHTTPResponse, newErr - } - if localVarHTTPResponse.StatusCode == 500 { - var v ModelsInternalServerError - err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr.error = err.Error() - return localVarReturnValue, localVarHTTPResponse, newErr - } - newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) - newErr.model = v - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type ApiListStatuesRequest struct { - ctx context.Context - ApiService *StatusApiService -} - -func (r ApiListStatuesRequest) Execute() ([]ModelsStatus, *http.Response, error) { - return r.ApiService.ListStatuesExecute(r) -} - -/* -ListStatues List Statues - -Lists all Statues - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @return ApiListStatuesRequest -*/ -func (a *StatusApiService) ListStatues(ctx context.Context) ApiListStatuesRequest { - return ApiListStatuesRequest{ - ApiService: a, - ctx: ctx, - } -} - -// Execute executes the request -// -// @return []ModelsStatus -func (a *StatusApiService) ListStatuesExecute(r ApiListStatuesRequest) ([]ModelsStatus, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue []ModelsStatus - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "StatusApiService.ListStatues") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/api/statues" - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - if localVarHTTPResponse.StatusCode == 401 { - var v ModelsBaseError - err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr.error = err.Error() - return localVarReturnValue, localVarHTTPResponse, newErr - } - newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) - newErr.model = v - return localVarReturnValue, localVarHTTPResponse, newErr - } - if localVarHTTPResponse.StatusCode == 429 { - var v ModelsBaseError - err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr.error = err.Error() - return localVarReturnValue, localVarHTTPResponse, newErr - } - newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) - newErr.model = v - return localVarReturnValue, localVarHTTPResponse, newErr - } - if localVarHTTPResponse.StatusCode == 500 { - var v ModelsInternalServerError - err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr.error = err.Error() - return localVarReturnValue, localVarHTTPResponse, newErr - } - newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) - newErr.model = v - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - type ApiStatusIdGetRequest struct { ctx context.Context ApiService *StatusApiService diff --git a/internal/client/client.go b/internal/client/client.go index 4ea163baa..1d09b0ac0 100644 --- a/internal/client/client.go +++ b/internal/client/client.go @@ -74,6 +74,8 @@ type APIClient struct { StatusApi *StatusApiService + StatusesApi *StatusesApiService + UsersApi *UsersApiService VPCApi *VPCApiService @@ -107,6 +109,7 @@ func NewAPIClient(cfg *Configuration) *APIClient { c.ServiceNetworkApi = (*ServiceNetworkApiService)(&c.common) c.SitesApi = (*SitesApiService)(&c.common) c.StatusApi = (*StatusApiService)(&c.common) + c.StatusesApi = (*StatusesApiService)(&c.common) c.UsersApi = (*UsersApiService)(&c.common) c.VPCApi = (*VPCApiService)(&c.common) diff --git a/internal/docs/docs.go b/internal/docs/docs.go index 52e3d33b1..bd6fe6dc0 100644 --- a/internal/docs/docs.go +++ b/internal/docs/docs.go @@ -2662,7 +2662,7 @@ const docTemplate = `{ }, "/api/statues": { "get": { - "description": "Lists all Statues", + "description": "Lists all Statuses", "consumes": [ "application/json" ], @@ -2670,10 +2670,10 @@ const docTemplate = `{ "application/json" ], "tags": [ - "status" + "Statuses" ], - "summary": "List Statues", - "operationId": "ListStatues", + "summary": "List Statuses", + "operationId": "ListStatuses", "responses": { "200": { "description": "OK", @@ -2715,18 +2715,18 @@ const docTemplate = `{ "application/json" ], "tags": [ - "status" + "Statuses" ], - "summary": "Add Status", + "summary": "Add Statuses", "operationId": "CreateStatus", "parameters": [ { - "description": "Status", + "description": "Add Status", "name": "Status", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/models.Status" + "$ref": "#/definitions/models.AddStatus" } } ], @@ -4050,6 +4050,26 @@ const docTemplate = `{ } } }, + "models.AddStatus": { + "type": "object", + "properties": { + "hostname": { + "type": "string" + }, + "is_reachable": { + "type": "boolean" + }, + "latency": { + "type": "string" + }, + "method": { + "type": "string" + }, + "wg_ip": { + "type": "string" + } + } + }, "models.AddVPC": { "type": "object", "properties": { diff --git a/internal/docs/swagger.json b/internal/docs/swagger.json index ad00eec66..55bb67fa9 100644 --- a/internal/docs/swagger.json +++ b/internal/docs/swagger.json @@ -2655,7 +2655,7 @@ }, "/api/statues": { "get": { - "description": "Lists all Statues", + "description": "Lists all Statuses", "consumes": [ "application/json" ], @@ -2663,10 +2663,10 @@ "application/json" ], "tags": [ - "status" + "Statuses" ], - "summary": "List Statues", - "operationId": "ListStatues", + "summary": "List Statuses", + "operationId": "ListStatuses", "responses": { "200": { "description": "OK", @@ -2708,18 +2708,18 @@ "application/json" ], "tags": [ - "status" + "Statuses" ], - "summary": "Add Status", + "summary": "Add Statuses", "operationId": "CreateStatus", "parameters": [ { - "description": "Status", + "description": "Add Status", "name": "Status", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/models.Status" + "$ref": "#/definitions/models.AddStatus" } } ], @@ -4043,6 +4043,26 @@ } } }, + "models.AddStatus": { + "type": "object", + "properties": { + "hostname": { + "type": "string" + }, + "is_reachable": { + "type": "boolean" + }, + "latency": { + "type": "string" + }, + "method": { + "type": "string" + }, + "wg_ip": { + "type": "string" + } + } + }, "models.AddVPC": { "type": "object", "properties": { diff --git a/internal/docs/swagger.yaml b/internal/docs/swagger.yaml index 540da4db0..b2ca9cec1 100644 --- a/internal/docs/swagger.yaml +++ b/internal/docs/swagger.yaml @@ -122,6 +122,19 @@ definitions: example: 694aa002-5d19-495e-980b-3d8fd508ea10 type: string type: object + models.AddStatus: + properties: + hostname: + type: string + is_reachable: + type: boolean + latency: + type: string + method: + type: string + wg_ip: + type: string + type: object models.AddVPC: properties: description: @@ -2514,8 +2527,8 @@ paths: get: consumes: - application/json - description: Lists all Statues - operationId: ListStatues + description: Lists all Statuses + operationId: ListStatuses produces: - application/json responses: @@ -2537,9 +2550,9 @@ paths: description: Internal Server Error schema: $ref: '#/definitions/models.InternalServerError' - summary: List Statues + summary: List Statuses tags: - - status + - Statuses /api/status: post: consumes: @@ -2547,12 +2560,12 @@ paths: description: Adds a new status operationId: CreateStatus parameters: - - description: Status + - description: Add Status in: body name: Status required: true schema: - $ref: '#/definitions/models.Status' + $ref: '#/definitions/models.AddStatus' produces: - application/json responses: @@ -2580,9 +2593,9 @@ paths: description: Internal Server Error schema: $ref: '#/definitions/models.InternalServerError' - summary: Add Status + summary: Add Statuses tags: - - status + - Statuses /api/users: get: consumes: diff --git a/internal/handlers/status.go b/internal/handlers/status.go index d3c440168..f95c352b2 100644 --- a/internal/handlers/status.go +++ b/internal/handlers/status.go @@ -2,37 +2,23 @@ package handlers import ( "errors" - //"fmt" - //"github.com/nexodus-io/nexodus/internal/handlers/fetchmgr" - "net/http" - //"time" - //"context" - "github.com/gin-gonic/gin" "github.com/google/uuid" + "github.com/nexodus-io/nexodus/internal/models" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" - - //"github.com/google/uuid" - //"github.com/nexodus-io/nexodus/internal/database" - "github.com/nexodus-io/nexodus/internal/models" - //"github.com/nexodus-io/nexodus/internal/util" - //"github.com/nexodus-io/nexodus/internal/wgcrypto" - //"go.opentelemetry.io/otel/attribute" - //"go.opentelemetry.io/otel/trace" - //"golang.zx2c4.com/wireguard/wgctrl/wgtypes" "gorm.io/gorm" - //"gorm.io/gorm/clause" + "net/http" ) -// CreateStatus handles adding a new device -// @Summary Add Status +// CreateStatus handles adding a new status +// @Summary Add Statuses // @Id CreateStatus -// @Tags status +// @Tags Statuses // @Description Adds a new status // @Accept json // @Produce json -// @Param Status body models.Status true "Status" +// @Param Status body models.AddStatus true "Add Status" // @Success 201 {object} models.Status // @Failure 400 {object} models.BaseError // @Failure 401 {object} models.BaseError @@ -127,11 +113,11 @@ func (api *API) GetStatus(c *gin.Context) { c.JSON(http.StatusOK, status) } -// ListStatues lists all Statues -// @Summary List Statues -// @Description Lists all Statues -// @Id ListStatues -// @Tags status +// ListStatues Lists all statuses +// @Summary List Statuses +// @Description Lists all Statuses +// @Id ListStatuses +// @Tags Statuses // @Accept json // @Produce json // @Success 200 {object} []models.Status diff --git a/internal/models/status.go b/internal/models/status.go index 4f960004c..8ee085ae4 100644 --- a/internal/models/status.go +++ b/internal/models/status.go @@ -13,6 +13,14 @@ type Status struct { Method string `json:"method"` } +type AddStatus struct { + WgIP string `json:"wg_ip"` + IsReachable bool `json:"is_reachable"` + Hostname string `json:"hostname"` + Latency string `json:"latency"` + Method string `json:"method"` +} + /*type UpdateStatus struct{ IsReachable bool `json:"is_reachable"` Latency string `json:""` diff --git a/internal/nexodus/ctlconnectivty.go b/internal/nexodus/ctlconnectivty.go index 6a20571f9..a763c2159 100644 --- a/internal/nexodus/ctlconnectivty.go +++ b/internal/nexodus/ctlconnectivty.go @@ -6,11 +6,16 @@ import ( "net" "github.com/nexodus-io/nexodus/internal/api" + "github.com/nexodus-io/nexodus/internal/client" "go.uber.org/zap" - "bytes" - "net/http" + //"bytes" + //"net/http" + "context" + //"errors" + //"io" + //"net/http" ) const ( @@ -135,37 +140,41 @@ func (nx *Nexodus) probeConnectivity(peersByKey map[string]api.KeepaliveStatus, } } - go nx.sendPeerData(peerConnResultsMap) + _, err := nx.createStatusesOperation(peerConnResultsMap) + + if err != nil { + fmt.Println("Error:", err) + } return peerConnResultsMap } -func (nx *Nexodus) sendPeerData(resultsMap map[string]api.KeepaliveStatus) { +func (nx *Nexodus) createStatusesOperation(resultsMap map[string]api.KeepaliveStatus) (string, error) { - peerResultsData, err := json.Marshal(resultsMap) - if err != nil { - fmt.Println("Error marshalling JSON:", err) - return - } + var err error - resp, err := http.Post("/status", "application/json", bytes.NewBuffer(peerResultsData)) - if err != nil { - fmt.Println("Error sending POST request:", err) - return - } - defer resp.Body.Close() + for _, status := range resultsMap { - if resp.StatusCode != http.StatusOK { - fmt.Println("Unexpected response status code:", resp.StatusCode) - // Handle error response if needed - return - } + hostname := status.Hostname + isReachable := status.IsReachable + latency := status.Latency + method := status.Method + wgip := status.WgIP + + newStatus := client.ModelsAddStatus{ + Hostname: &hostname, + IsReachable: &isReachable, + Latency: &latency, + Method: &method, + WgIp: &wgip, + } + _, _, err = nx.client.StatusesApi.CreateStatus(context.Background()).Status(newStatus).Execute() + + if err != nil { + return "New status error", fmt.Errorf("error: %w", err) + } - var responseData map[string]interface{} - if err := json.NewDecoder(resp.Body).Decode(&responseData); err != nil { - fmt.Println("Error decoding JSON response:", err) - return } - fmt.Println("Response:", responseData) + return "", nil } diff --git a/ui/index.html b/ui/index.html index 0900efdd8..8b4a07471 100644 --- a/ui/index.html +++ b/ui/index.html @@ -6,23 +6,82 @@ - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + diff --git a/ui/package-lock.json b/ui/package-lock.json index 09a99b926..1b84e6bbf 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -17,7 +17,7 @@ "d3": "^7.9.0", "echarts": "^5.5.0", "echarts-for-react": "^3.0.2", - "ra-data-simple-rest": "^4.16.11", + "ra-data-simple-rest": "^4.16.12", "react": "^18.2.0", "react-admin": "^4.16.12", "react-dom": "^18.2.0", diff --git a/ui/playwright.config.ts b/ui/playwright.config.ts index 38c7591e7..bd61f005f 100644 --- a/ui/playwright.config.ts +++ b/ui/playwright.config.ts @@ -1,4 +1,4 @@ -import { defineConfig, devices } from '@playwright/test'; +import { defineConfig, devices } from "@playwright/test"; import env from "./tests/env"; /** @@ -11,7 +11,7 @@ import env from "./tests/env"; * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ - testDir: './tests', + testDir: "./tests", /* Run tests in files in parallel */ fullyParallel: true, /* Fail the build on CI if you accidentally left test.only in the source code. */ @@ -21,28 +21,28 @@ export default defineConfig({ /* Opt out of parallel tests on CI. */ workers: process.env.CI ? 1 : undefined, /* Reporter to use. See https://playwright.dev/docs/test-reporters */ - reporter: 'html', + reporter: "html", /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ use: { /* Base URL to use in actions like `await page.goto('/')`. */ baseURL: env.url, /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ - trace: 'on-first-retry', + trace: "on-first-retry", // Capture screenshot after each test failure. - screenshot: 'only-on-failure', + screenshot: "only-on-failure", // Record video only when retrying a test for the first time. - video: 'on-first-retry' + video: "on-first-retry", }, /* Configure projects for major browsers */ projects: [ { - name: 'chromium', - use: { ...devices['Desktop Chrome'] }, + name: "chromium", + use: { ...devices["Desktop Chrome"] }, }, { - name: 'webkit', - use: { ...devices['Desktop Safari'] }, + name: "webkit", + use: { ...devices["Desktop Safari"] }, }, ], }); diff --git a/ui/src/App.tsx b/ui/src/App.tsx index bf8c94e7a..3f97d8d7b 100644 --- a/ui/src/App.tsx +++ b/ui/src/App.tsx @@ -76,7 +76,7 @@ const App = () => { /> } /> - } /> + } /> { icon={DeviceIcon} edit={DeviceEdit} recordRepresentation={(record) => `${record.hostname}`} - /> + /> { const [isOpenRelay, setIsOpenRelay] = useState(false); const togglePopupDevice = () => { setIsOpenDevice(!isOpenDevice); - } + }; const togglePopupRelay = () => { setIsOpenRelay(!isOpenRelay); - } - + }; + return (
{ -
-
- - {isOpenDevice && ( - - - - - Hostname - IP Address - Latency (ms) - Peering Method - Connection Status - - - - {deviceRows.map((row, index) => ( - - - {row.hostname} - - {row.ipAddress} - {row.latency} - {row.peeringMethod} - {row.connectionStatus} - - ))} - -
-
- )} -
-
- - {isOpenRelay && ( - - - - - Hostname - IP Address - Latency (ms) - Peering Method - Connection Status - - - - {relayRows.map((row, index) => ( - - - {row.hostname} - - {row.ipAddress} - {row.latency} - {row.peeringMethod} - {row.connectionStatus} - - ))} - -
-
- )} -
+
+
+ + {isOpenDevice && ( + + + + + Hostname + IP Address + Latency (ms) + Peering Method + Connection Status + + + + {deviceRows.map((row, index) => ( + + + {row.hostname} + + + {row.ipAddress} + + + {row.latency} + + + {row.peeringMethod} + + + {row.connectionStatus} + + + ))} + +
+
+ )} +
+
+ + {isOpenRelay && ( + + + + + Hostname + IP Address + Latency (ms) + Peering Method + Connection Status + + + + {relayRows.map((row, index) => ( + + + {row.hostname} + + + {row.ipAddress} + + + {row.latency} + + + {row.peeringMethod} + + + {row.connectionStatus} + + + ))} + +
+
+ )}
+
); }; const styles = { device: { - fontSize: '12px', - fontWeight: 'bold', - padding: '9px 16px', + fontSize: "12px", + fontWeight: "bold", + padding: "9px 16px", //borderColor: 'green', //borderWidth: 2, - border: 'none', - borderTopRightRadius: '20px', - outline: '2px solid green', - boxShadow: '0 0 5px 2px rgba(0, 255, 0, 0.5)', - display: 'flex', - alignItems: 'center', + border: "none", + borderTopRightRadius: "20px", + outline: "2px solid green", + boxShadow: "0 0 5px 2px rgba(0, 255, 0, 0.5)", + display: "flex", + alignItems: "center", }, relay: { - fontSize: '12px', - fontWeight: 'bold', - padding: '9px 16px', + fontSize: "12px", + fontWeight: "bold", + padding: "9px 16px", //borderColor: 'red', //borderWidth: 2, - border: 'none', - borderRadius: '50px', - outline: '2px solid red', - boxShadow: '0 0 5px 2px rgba(255, 0, 0, 0.5)', - display: 'flex', - alignItems: 'center', + border: "none", + borderRadius: "50px", + outline: "2px solid red", + boxShadow: "0 0 5px 2px rgba(255, 0, 0, 0.5)", + display: "flex", + alignItems: "center", }, ip: { - fontSize: '12px', - fontWeight: 'bold', - padding: '12px 16px', - marginLeft: '10px', + fontSize: "12px", + fontWeight: "bold", + padding: "12px 16px", + marginLeft: "10px", }, onlineIcon: { - color: 'green', - fontSize: '20px', - marginLeft: '10px', + color: "green", + fontSize: "20px", + marginLeft: "10px", }, offlineIcon: { - color: 'red', - fontSize: '20px', - marginLeft: '10px', + color: "red", + fontSize: "20px", + marginLeft: "10px", }, }; diff --git a/ui/src/setupTests (copy).ts b/ui/src/setupTests (copy).ts deleted file mode 100644 index 3b47e5515..000000000 --- a/ui/src/setupTests (copy).ts +++ /dev/null @@ -1,2 +0,0 @@ -//declare module 'd3'; - diff --git a/ui/src/setupTests.ts b/ui/src/setupTests.ts index ac3646ac9..1dd407a63 100644 --- a/ui/src/setupTests.ts +++ b/ui/src/setupTests.ts @@ -2,4 +2,4 @@ // allows you to do things like: // expect(element).toHaveTextContent(/react/i) // learn more: https://github.com/testing-library/jest-dom -//import "@testing-library/jest-dom"; +import "@testing-library/jest-dom"; diff --git a/ui/tests-examples/demo-todo-app.spec.ts b/ui/tests-examples/demo-todo-app.spec.ts index 2fd6016fe..d36a1a2c0 100644 --- a/ui/tests-examples/demo-todo-app.spec.ts +++ b/ui/tests-examples/demo-todo-app.spec.ts @@ -1,75 +1,77 @@ -import { test, expect, type Page } from '@playwright/test'; +import { test, expect, type Page } from "@playwright/test"; test.beforeEach(async ({ page }) => { - await page.goto('https://demo.playwright.dev/todomvc'); + await page.goto("https://demo.playwright.dev/todomvc"); }); const TODO_ITEMS = [ - 'buy some cheese', - 'feed the cat', - 'book a doctors appointment' + "buy some cheese", + "feed the cat", + "book a doctors appointment", ]; -test.describe('New Todo', () => { - test('should allow me to add todo items', async ({ page }) => { +test.describe("New Todo", () => { + test("should allow me to add todo items", async ({ page }) => { // create a new todo locator - const newTodo = page.getByPlaceholder('What needs to be done?'); + const newTodo = page.getByPlaceholder("What needs to be done?"); // Create 1st todo. await newTodo.fill(TODO_ITEMS[0]); - await newTodo.press('Enter'); + await newTodo.press("Enter"); // Make sure the list only has one todo item. - await expect(page.getByTestId('todo-title')).toHaveText([ - TODO_ITEMS[0] - ]); + await expect(page.getByTestId("todo-title")).toHaveText([TODO_ITEMS[0]]); // Create 2nd todo. await newTodo.fill(TODO_ITEMS[1]); - await newTodo.press('Enter'); + await newTodo.press("Enter"); // Make sure the list now has two todo items. - await expect(page.getByTestId('todo-title')).toHaveText([ + await expect(page.getByTestId("todo-title")).toHaveText([ TODO_ITEMS[0], - TODO_ITEMS[1] + TODO_ITEMS[1], ]); await checkNumberOfTodosInLocalStorage(page, 2); }); - test('should clear text input field when an item is added', async ({ page }) => { + test("should clear text input field when an item is added", async ({ + page, + }) => { // create a new todo locator - const newTodo = page.getByPlaceholder('What needs to be done?'); + const newTodo = page.getByPlaceholder("What needs to be done?"); // Create one todo item. await newTodo.fill(TODO_ITEMS[0]); - await newTodo.press('Enter'); + await newTodo.press("Enter"); // Check that input is empty. await expect(newTodo).toBeEmpty(); await checkNumberOfTodosInLocalStorage(page, 1); }); - test('should append new items to the bottom of the list', async ({ page }) => { + test("should append new items to the bottom of the list", async ({ + page, + }) => { // Create 3 items. await createDefaultTodos(page); // create a todo count locator - const todoCount = page.getByTestId('todo-count') - + const todoCount = page.getByTestId("todo-count"); + // Check test using different methods. - await expect(page.getByText('3 items left')).toBeVisible(); - await expect(todoCount).toHaveText('3 items left'); - await expect(todoCount).toContainText('3'); + await expect(page.getByText("3 items left")).toBeVisible(); + await expect(todoCount).toHaveText("3 items left"); + await expect(todoCount).toContainText("3"); await expect(todoCount).toHaveText(/3/); // Check all items in one call. - await expect(page.getByTestId('todo-title')).toHaveText(TODO_ITEMS); + await expect(page.getByTestId("todo-title")).toHaveText(TODO_ITEMS); await checkNumberOfTodosInLocalStorage(page, 3); }); }); -test.describe('Mark all as completed', () => { +test.describe("Mark all as completed", () => { test.beforeEach(async ({ page }) => { await createDefaultTodos(page); await checkNumberOfTodosInLocalStorage(page, 3); @@ -79,39 +81,47 @@ test.describe('Mark all as completed', () => { await checkNumberOfTodosInLocalStorage(page, 3); }); - test('should allow me to mark all items as completed', async ({ page }) => { + test("should allow me to mark all items as completed", async ({ page }) => { // Complete all todos. - await page.getByLabel('Mark all as complete').check(); + await page.getByLabel("Mark all as complete").check(); // Ensure all todos have 'completed' class. - await expect(page.getByTestId('todo-item')).toHaveClass(['completed', 'completed', 'completed']); + await expect(page.getByTestId("todo-item")).toHaveClass([ + "completed", + "completed", + "completed", + ]); await checkNumberOfCompletedTodosInLocalStorage(page, 3); }); - test('should allow me to clear the complete state of all items', async ({ page }) => { - const toggleAll = page.getByLabel('Mark all as complete'); + test("should allow me to clear the complete state of all items", async ({ + page, + }) => { + const toggleAll = page.getByLabel("Mark all as complete"); // Check and then immediately uncheck. await toggleAll.check(); await toggleAll.uncheck(); // Should be no completed classes. - await expect(page.getByTestId('todo-item')).toHaveClass(['', '', '']); + await expect(page.getByTestId("todo-item")).toHaveClass(["", "", ""]); }); - test('complete all checkbox should update state when items are completed / cleared', async ({ page }) => { - const toggleAll = page.getByLabel('Mark all as complete'); + test("complete all checkbox should update state when items are completed / cleared", async ({ + page, + }) => { + const toggleAll = page.getByLabel("Mark all as complete"); await toggleAll.check(); await expect(toggleAll).toBeChecked(); await checkNumberOfCompletedTodosInLocalStorage(page, 3); // Uncheck first todo. - const firstTodo = page.getByTestId('todo-item').nth(0); - await firstTodo.getByRole('checkbox').uncheck(); + const firstTodo = page.getByTestId("todo-item").nth(0); + await firstTodo.getByRole("checkbox").uncheck(); // Reuse toggleAll locator and make sure its not checked. await expect(toggleAll).not.toBeChecked(); - await firstTodo.getByRole('checkbox').check(); + await firstTodo.getByRole("checkbox").check(); await checkNumberOfCompletedTodosInLocalStorage(page, 3); // Assert the toggle all is checked again. @@ -119,205 +129,236 @@ test.describe('Mark all as completed', () => { }); }); -test.describe('Item', () => { - - test('should allow me to mark items as complete', async ({ page }) => { +test.describe("Item", () => { + test("should allow me to mark items as complete", async ({ page }) => { // create a new todo locator - const newTodo = page.getByPlaceholder('What needs to be done?'); + const newTodo = page.getByPlaceholder("What needs to be done?"); // Create two items. for (const item of TODO_ITEMS.slice(0, 2)) { await newTodo.fill(item); - await newTodo.press('Enter'); + await newTodo.press("Enter"); } // Check first item. - const firstTodo = page.getByTestId('todo-item').nth(0); - await firstTodo.getByRole('checkbox').check(); - await expect(firstTodo).toHaveClass('completed'); + const firstTodo = page.getByTestId("todo-item").nth(0); + await firstTodo.getByRole("checkbox").check(); + await expect(firstTodo).toHaveClass("completed"); // Check second item. - const secondTodo = page.getByTestId('todo-item').nth(1); - await expect(secondTodo).not.toHaveClass('completed'); - await secondTodo.getByRole('checkbox').check(); + const secondTodo = page.getByTestId("todo-item").nth(1); + await expect(secondTodo).not.toHaveClass("completed"); + await secondTodo.getByRole("checkbox").check(); // Assert completed class. - await expect(firstTodo).toHaveClass('completed'); - await expect(secondTodo).toHaveClass('completed'); + await expect(firstTodo).toHaveClass("completed"); + await expect(secondTodo).toHaveClass("completed"); }); - test('should allow me to un-mark items as complete', async ({ page }) => { + test("should allow me to un-mark items as complete", async ({ page }) => { // create a new todo locator - const newTodo = page.getByPlaceholder('What needs to be done?'); + const newTodo = page.getByPlaceholder("What needs to be done?"); // Create two items. for (const item of TODO_ITEMS.slice(0, 2)) { await newTodo.fill(item); - await newTodo.press('Enter'); + await newTodo.press("Enter"); } - const firstTodo = page.getByTestId('todo-item').nth(0); - const secondTodo = page.getByTestId('todo-item').nth(1); - const firstTodoCheckbox = firstTodo.getByRole('checkbox'); + const firstTodo = page.getByTestId("todo-item").nth(0); + const secondTodo = page.getByTestId("todo-item").nth(1); + const firstTodoCheckbox = firstTodo.getByRole("checkbox"); await firstTodoCheckbox.check(); - await expect(firstTodo).toHaveClass('completed'); - await expect(secondTodo).not.toHaveClass('completed'); + await expect(firstTodo).toHaveClass("completed"); + await expect(secondTodo).not.toHaveClass("completed"); await checkNumberOfCompletedTodosInLocalStorage(page, 1); await firstTodoCheckbox.uncheck(); - await expect(firstTodo).not.toHaveClass('completed'); - await expect(secondTodo).not.toHaveClass('completed'); + await expect(firstTodo).not.toHaveClass("completed"); + await expect(secondTodo).not.toHaveClass("completed"); await checkNumberOfCompletedTodosInLocalStorage(page, 0); }); - test('should allow me to edit an item', async ({ page }) => { + test("should allow me to edit an item", async ({ page }) => { await createDefaultTodos(page); - const todoItems = page.getByTestId('todo-item'); + const todoItems = page.getByTestId("todo-item"); const secondTodo = todoItems.nth(1); await secondTodo.dblclick(); - await expect(secondTodo.getByRole('textbox', { name: 'Edit' })).toHaveValue(TODO_ITEMS[1]); - await secondTodo.getByRole('textbox', { name: 'Edit' }).fill('buy some sausages'); - await secondTodo.getByRole('textbox', { name: 'Edit' }).press('Enter'); + await expect(secondTodo.getByRole("textbox", { name: "Edit" })).toHaveValue( + TODO_ITEMS[1], + ); + await secondTodo + .getByRole("textbox", { name: "Edit" }) + .fill("buy some sausages"); + await secondTodo.getByRole("textbox", { name: "Edit" }).press("Enter"); // Explicitly assert the new text value. await expect(todoItems).toHaveText([ TODO_ITEMS[0], - 'buy some sausages', - TODO_ITEMS[2] + "buy some sausages", + TODO_ITEMS[2], ]); - await checkTodosInLocalStorage(page, 'buy some sausages'); + await checkTodosInLocalStorage(page, "buy some sausages"); }); }); -test.describe('Editing', () => { +test.describe("Editing", () => { test.beforeEach(async ({ page }) => { await createDefaultTodos(page); await checkNumberOfTodosInLocalStorage(page, 3); }); - test('should hide other controls when editing', async ({ page }) => { - const todoItem = page.getByTestId('todo-item').nth(1); + test("should hide other controls when editing", async ({ page }) => { + const todoItem = page.getByTestId("todo-item").nth(1); await todoItem.dblclick(); - await expect(todoItem.getByRole('checkbox')).not.toBeVisible(); - await expect(todoItem.locator('label', { - hasText: TODO_ITEMS[1], - })).not.toBeVisible(); + await expect(todoItem.getByRole("checkbox")).not.toBeVisible(); + await expect( + todoItem.locator("label", { + hasText: TODO_ITEMS[1], + }), + ).not.toBeVisible(); await checkNumberOfTodosInLocalStorage(page, 3); }); - test('should save edits on blur', async ({ page }) => { - const todoItems = page.getByTestId('todo-item'); + test("should save edits on blur", async ({ page }) => { + const todoItems = page.getByTestId("todo-item"); await todoItems.nth(1).dblclick(); - await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill('buy some sausages'); - await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).dispatchEvent('blur'); + await todoItems + .nth(1) + .getByRole("textbox", { name: "Edit" }) + .fill("buy some sausages"); + await todoItems + .nth(1) + .getByRole("textbox", { name: "Edit" }) + .dispatchEvent("blur"); await expect(todoItems).toHaveText([ TODO_ITEMS[0], - 'buy some sausages', + "buy some sausages", TODO_ITEMS[2], ]); - await checkTodosInLocalStorage(page, 'buy some sausages'); + await checkTodosInLocalStorage(page, "buy some sausages"); }); - test('should trim entered text', async ({ page }) => { - const todoItems = page.getByTestId('todo-item'); + test("should trim entered text", async ({ page }) => { + const todoItems = page.getByTestId("todo-item"); await todoItems.nth(1).dblclick(); - await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill(' buy some sausages '); - await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Enter'); + await todoItems + .nth(1) + .getByRole("textbox", { name: "Edit" }) + .fill(" buy some sausages "); + await todoItems + .nth(1) + .getByRole("textbox", { name: "Edit" }) + .press("Enter"); await expect(todoItems).toHaveText([ TODO_ITEMS[0], - 'buy some sausages', + "buy some sausages", TODO_ITEMS[2], ]); - await checkTodosInLocalStorage(page, 'buy some sausages'); + await checkTodosInLocalStorage(page, "buy some sausages"); }); - test('should remove the item if an empty text string was entered', async ({ page }) => { - const todoItems = page.getByTestId('todo-item'); + test("should remove the item if an empty text string was entered", async ({ + page, + }) => { + const todoItems = page.getByTestId("todo-item"); await todoItems.nth(1).dblclick(); - await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill(''); - await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Enter'); + await todoItems.nth(1).getByRole("textbox", { name: "Edit" }).fill(""); + await todoItems + .nth(1) + .getByRole("textbox", { name: "Edit" }) + .press("Enter"); - await expect(todoItems).toHaveText([ - TODO_ITEMS[0], - TODO_ITEMS[2], - ]); + await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[2]]); }); - test('should cancel edits on escape', async ({ page }) => { - const todoItems = page.getByTestId('todo-item'); + test("should cancel edits on escape", async ({ page }) => { + const todoItems = page.getByTestId("todo-item"); await todoItems.nth(1).dblclick(); - await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill('buy some sausages'); - await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Escape'); + await todoItems + .nth(1) + .getByRole("textbox", { name: "Edit" }) + .fill("buy some sausages"); + await todoItems + .nth(1) + .getByRole("textbox", { name: "Edit" }) + .press("Escape"); await expect(todoItems).toHaveText(TODO_ITEMS); }); }); -test.describe('Counter', () => { - test('should display the current number of todo items', async ({ page }) => { +test.describe("Counter", () => { + test("should display the current number of todo items", async ({ page }) => { // create a new todo locator - const newTodo = page.getByPlaceholder('What needs to be done?'); - + const newTodo = page.getByPlaceholder("What needs to be done?"); + // create a todo count locator - const todoCount = page.getByTestId('todo-count') + const todoCount = page.getByTestId("todo-count"); await newTodo.fill(TODO_ITEMS[0]); - await newTodo.press('Enter'); + await newTodo.press("Enter"); - await expect(todoCount).toContainText('1'); + await expect(todoCount).toContainText("1"); await newTodo.fill(TODO_ITEMS[1]); - await newTodo.press('Enter'); - await expect(todoCount).toContainText('2'); + await newTodo.press("Enter"); + await expect(todoCount).toContainText("2"); await checkNumberOfTodosInLocalStorage(page, 2); }); }); -test.describe('Clear completed button', () => { +test.describe("Clear completed button", () => { test.beforeEach(async ({ page }) => { await createDefaultTodos(page); }); - test('should display the correct text', async ({ page }) => { - await page.locator('.todo-list li .toggle').first().check(); - await expect(page.getByRole('button', { name: 'Clear completed' })).toBeVisible(); + test("should display the correct text", async ({ page }) => { + await page.locator(".todo-list li .toggle").first().check(); + await expect( + page.getByRole("button", { name: "Clear completed" }), + ).toBeVisible(); }); - test('should remove completed items when clicked', async ({ page }) => { - const todoItems = page.getByTestId('todo-item'); - await todoItems.nth(1).getByRole('checkbox').check(); - await page.getByRole('button', { name: 'Clear completed' }).click(); + test("should remove completed items when clicked", async ({ page }) => { + const todoItems = page.getByTestId("todo-item"); + await todoItems.nth(1).getByRole("checkbox").check(); + await page.getByRole("button", { name: "Clear completed" }).click(); await expect(todoItems).toHaveCount(2); await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[2]]); }); - test('should be hidden when there are no items that are completed', async ({ page }) => { - await page.locator('.todo-list li .toggle').first().check(); - await page.getByRole('button', { name: 'Clear completed' }).click(); - await expect(page.getByRole('button', { name: 'Clear completed' })).toBeHidden(); + test("should be hidden when there are no items that are completed", async ({ + page, + }) => { + await page.locator(".todo-list li .toggle").first().check(); + await page.getByRole("button", { name: "Clear completed" }).click(); + await expect( + page.getByRole("button", { name: "Clear completed" }), + ).toBeHidden(); }); }); -test.describe('Persistence', () => { - test('should persist its data', async ({ page }) => { +test.describe("Persistence", () => { + test("should persist its data", async ({ page }) => { // create a new todo locator - const newTodo = page.getByPlaceholder('What needs to be done?'); + const newTodo = page.getByPlaceholder("What needs to be done?"); for (const item of TODO_ITEMS.slice(0, 2)) { await newTodo.fill(item); - await newTodo.press('Enter'); + await newTodo.press("Enter"); } - const todoItems = page.getByTestId('todo-item'); - const firstTodoCheck = todoItems.nth(0).getByRole('checkbox'); + const todoItems = page.getByTestId("todo-item"); + const firstTodoCheck = todoItems.nth(0).getByRole("checkbox"); await firstTodoCheck.check(); await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[1]]); await expect(firstTodoCheck).toBeChecked(); - await expect(todoItems).toHaveClass(['completed', '']); + await expect(todoItems).toHaveClass(["completed", ""]); // Ensure there is 1 completed item. await checkNumberOfCompletedTodosInLocalStorage(page, 1); @@ -326,11 +367,11 @@ test.describe('Persistence', () => { await page.reload(); await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[1]]); await expect(firstTodoCheck).toBeChecked(); - await expect(todoItems).toHaveClass(['completed', '']); + await expect(todoItems).toHaveClass(["completed", ""]); }); }); -test.describe('Routing', () => { +test.describe("Routing", () => { test.beforeEach(async ({ page }) => { await createDefaultTodos(page); // make sure the app had a chance to save updated todos in storage @@ -339,33 +380,33 @@ test.describe('Routing', () => { await checkTodosInLocalStorage(page, TODO_ITEMS[0]); }); - test('should allow me to display active items', async ({ page }) => { - const todoItem = page.getByTestId('todo-item'); - await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check(); + test("should allow me to display active items", async ({ page }) => { + const todoItem = page.getByTestId("todo-item"); + await page.getByTestId("todo-item").nth(1).getByRole("checkbox").check(); await checkNumberOfCompletedTodosInLocalStorage(page, 1); - await page.getByRole('link', { name: 'Active' }).click(); + await page.getByRole("link", { name: "Active" }).click(); await expect(todoItem).toHaveCount(2); await expect(todoItem).toHaveText([TODO_ITEMS[0], TODO_ITEMS[2]]); }); - test('should respect the back button', async ({ page }) => { - const todoItem = page.getByTestId('todo-item'); - await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check(); + test("should respect the back button", async ({ page }) => { + const todoItem = page.getByTestId("todo-item"); + await page.getByTestId("todo-item").nth(1).getByRole("checkbox").check(); await checkNumberOfCompletedTodosInLocalStorage(page, 1); - await test.step('Showing all items', async () => { - await page.getByRole('link', { name: 'All' }).click(); + await test.step("Showing all items", async () => { + await page.getByRole("link", { name: "All" }).click(); await expect(todoItem).toHaveCount(3); }); - await test.step('Showing active items', async () => { - await page.getByRole('link', { name: 'Active' }).click(); + await test.step("Showing active items", async () => { + await page.getByRole("link", { name: "Active" }).click(); }); - await test.step('Showing completed items', async () => { - await page.getByRole('link', { name: 'Completed' }).click(); + await test.step("Showing completed items", async () => { + await page.getByRole("link", { name: "Completed" }).click(); }); await expect(todoItem).toHaveCount(1); @@ -375,63 +416,74 @@ test.describe('Routing', () => { await expect(todoItem).toHaveCount(3); }); - test('should allow me to display completed items', async ({ page }) => { - await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check(); + test("should allow me to display completed items", async ({ page }) => { + await page.getByTestId("todo-item").nth(1).getByRole("checkbox").check(); await checkNumberOfCompletedTodosInLocalStorage(page, 1); - await page.getByRole('link', { name: 'Completed' }).click(); - await expect(page.getByTestId('todo-item')).toHaveCount(1); + await page.getByRole("link", { name: "Completed" }).click(); + await expect(page.getByTestId("todo-item")).toHaveCount(1); }); - test('should allow me to display all items', async ({ page }) => { - await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check(); + test("should allow me to display all items", async ({ page }) => { + await page.getByTestId("todo-item").nth(1).getByRole("checkbox").check(); await checkNumberOfCompletedTodosInLocalStorage(page, 1); - await page.getByRole('link', { name: 'Active' }).click(); - await page.getByRole('link', { name: 'Completed' }).click(); - await page.getByRole('link', { name: 'All' }).click(); - await expect(page.getByTestId('todo-item')).toHaveCount(3); + await page.getByRole("link", { name: "Active" }).click(); + await page.getByRole("link", { name: "Completed" }).click(); + await page.getByRole("link", { name: "All" }).click(); + await expect(page.getByTestId("todo-item")).toHaveCount(3); }); - test('should highlight the currently applied filter', async ({ page }) => { - await expect(page.getByRole('link', { name: 'All' })).toHaveClass('selected'); - + test("should highlight the currently applied filter", async ({ page }) => { + await expect(page.getByRole("link", { name: "All" })).toHaveClass( + "selected", + ); + //create locators for active and completed links - const activeLink = page.getByRole('link', { name: 'Active' }); - const completedLink = page.getByRole('link', { name: 'Completed' }); + const activeLink = page.getByRole("link", { name: "Active" }); + const completedLink = page.getByRole("link", { name: "Completed" }); await activeLink.click(); // Page change - active items. - await expect(activeLink).toHaveClass('selected'); + await expect(activeLink).toHaveClass("selected"); await completedLink.click(); // Page change - completed items. - await expect(completedLink).toHaveClass('selected'); + await expect(completedLink).toHaveClass("selected"); }); }); async function createDefaultTodos(page: Page) { // create a new todo locator - const newTodo = page.getByPlaceholder('What needs to be done?'); + const newTodo = page.getByPlaceholder("What needs to be done?"); for (const item of TODO_ITEMS) { await newTodo.fill(item); - await newTodo.press('Enter'); + await newTodo.press("Enter"); } } async function checkNumberOfTodosInLocalStorage(page: Page, expected: number) { - return await page.waitForFunction(e => { - return JSON.parse(localStorage['react-todos']).length === e; + return await page.waitForFunction((e) => { + return JSON.parse(localStorage["react-todos"]).length === e; }, expected); } -async function checkNumberOfCompletedTodosInLocalStorage(page: Page, expected: number) { - return await page.waitForFunction(e => { - return JSON.parse(localStorage['react-todos']).filter((todo: any) => todo.completed).length === e; +async function checkNumberOfCompletedTodosInLocalStorage( + page: Page, + expected: number, +) { + return await page.waitForFunction((e) => { + return ( + JSON.parse(localStorage["react-todos"]).filter( + (todo: any) => todo.completed, + ).length === e + ); }, expected); } async function checkTodosInLocalStorage(page: Page, title: string) { - return await page.waitForFunction(t => { - return JSON.parse(localStorage['react-todos']).map((todo: any) => todo.title).includes(t); + return await page.waitForFunction((t) => { + return JSON.parse(localStorage["react-todos"]) + .map((todo: any) => todo.title) + .includes(t); }, title); } diff --git a/ui/tests-examples/example.spec.ts b/ui/tests-examples/example.spec.ts index 54a906a4e..b60fe7cd0 100644 --- a/ui/tests-examples/example.spec.ts +++ b/ui/tests-examples/example.spec.ts @@ -1,18 +1,20 @@ -import { test, expect } from '@playwright/test'; +import { test, expect } from "@playwright/test"; -test('has title', async ({ page }) => { - await page.goto('https://playwright.dev/'); +test("has title", async ({ page }) => { + await page.goto("https://playwright.dev/"); // Expect a title "to contain" a substring. await expect(page).toHaveTitle(/Playwright/); }); -test('get started link', async ({ page }) => { - await page.goto('https://playwright.dev/'); +test("get started link", async ({ page }) => { + await page.goto("https://playwright.dev/"); // Click the get started link. - await page.getByRole('link', { name: 'Get started' }).click(); + await page.getByRole("link", { name: "Get started" }).click(); // Expects page to have a heading with the name of Installation. - await expect(page.getByRole('heading', { name: 'Installation' })).toBeVisible(); + await expect( + page.getByRole("heading", { name: "Installation" }), + ).toBeVisible(); }); diff --git a/ui/tests/env.ts b/ui/tests/env.ts index c5038ccdb..fe53d1f64 100644 --- a/ui/tests/env.ts +++ b/ui/tests/env.ts @@ -1,7 +1,7 @@ import Dashboard from "../src/pages/Dashboard.js"; export default { - url: process.env.NEXODUS_URL || 'https://try.nexodus.127.0.0.1.nip.io', - username: process.env.NEXODUS_USERNAME || 'admin', - password: process.env.NEXODUS_PASSWORD || 'floofykittens', + url: process.env.NEXODUS_URL || "https://try.nexodus.127.0.0.1.nip.io", + username: process.env.NEXODUS_USERNAME || "admin", + password: process.env.NEXODUS_PASSWORD || "floofykittens", }; diff --git a/ui/tests/login.spec.ts b/ui/tests/login.spec.ts index 7e6a0d56e..93dec347d 100644 --- a/ui/tests/login.spec.ts +++ b/ui/tests/login.spec.ts @@ -1,61 +1,65 @@ -import { test, expect } from '@playwright/test'; +import { test, expect } from "@playwright/test"; import lib from "./env"; -test('Login takes you to the Dashboard', async ({ page }) => { -await page.goto('/#/login'); -await page.getByRole('button', { name: 'Login' }).click(); -await page.getByLabel('Username or email').fill(lib.username); -await page.getByLabel('Password').fill(lib.password); -await page.getByLabel('Password').press('Enter'); -// Validate Dashboard -await expect(page.getByText('Welcome to Nexodus')).toBeVisible(); -await page.getByRole('menuitem', { name: 'Dashboard' }).click(); -await expect(page.getByText('Welcome to Nexodus')).toBeVisible(); -// Validate Organizations -await page.getByRole('menuitem', { name: 'Organizations' }).click(); -await expect(page.locator('#react-admin-title').getByText('Organizations')).toBeVisible(); -// Validate VPCs -await page.getByRole('menuitem', { name: 'VPCs' }).click(); -await expect(page.getByText('default vpc')).toBeVisible(); -await page.getByRole('cell', { name: 'default vpc' }).click(); -await expect(page.getByText('100.64.0.0')).toBeVisible(); -// Validate Devices -await page.getByRole('menuitem', { name: 'Devices' }).click(); -await expect(page.locator('#react-admin-title').getByText('Devices')).toBeVisible(); -// Validate Sites -await page.getByRole('menuitem', { name: 'Sites' }).click(); -// Validate Invitations -await page.getByRole('menuitem', { name: 'Invitations' }).click(); -await expect(page.getByLabel('Create')).toBeVisible(); -await page.getByLabel('Create').click(); -await expect(page.getByLabel('Email Address *')).toBeVisible(); -// Validate Security Groups -await page.getByRole('menuitem', { name: 'Security Groups' }).click(); -await page.getByRole('cell', { name: 'default vpc security group' }).click(); -await page.getByRole('button', { name: 'Edit Rules' }).click(); -await page.getByRole('button', { name: 'Add Rule' }).click(); -await page.getByRole('combobox').first().click(); -await page.getByRole('option', { name: 'All ICMP' }).click(); -await page.getByRole('button', { name: 'Save Rules' }).click(); -await page.getByRole('tab', { name: 'Outbound Rules' }).click(); -await page.getByRole('button', { name: 'Add Rule' }).click(); -await page.getByRole('combobox').first().click(); -await page.getByRole('option', { name: 'All ICMP' }).click(); -await page.getByRole('button', { name: 'Save Rules' }).click(); -await page.getByRole('tab', { name: 'Inbound Rules' }).click(); -await expect(page.getByText('All ICMP')).toBeVisible(); -await page.getByRole('tab', { name: 'Outbound Rules' }).click(); -await expect(page.getByText('All ICMP')).toBeVisible(); -await page.getByRole('button', { name: 'Delete' }).click(); -await page.getByRole('tab', { name: 'Inbound Rules' }).click(); -await page.getByRole('button', { name: 'Delete' }).click(); -await page.getByRole('button', { name: 'Save Rules' }).click(); -// Validate Registration Keys -await page.getByRole('menuitem', { name: 'Registration Keys' }).click(); -await page.getByLabel('Create').click(); -await expect(page.getByLabel('Description')).toBeVisible(); -// Validate Logout -await page.getByLabel('Profile').click(); -await page.getByText('Logout').click(); -await expect(page.getByRole('button', { name: 'Login' })).toBeVisible(); +test("Login takes you to the Dashboard", async ({ page }) => { + await page.goto("/#/login"); + await page.getByRole("button", { name: "Login" }).click(); + await page.getByLabel("Username or email").fill(lib.username); + await page.getByLabel("Password").fill(lib.password); + await page.getByLabel("Password").press("Enter"); + // Validate Dashboard + await expect(page.getByText("Welcome to Nexodus")).toBeVisible(); + await page.getByRole("menuitem", { name: "Dashboard" }).click(); + await expect(page.getByText("Welcome to Nexodus")).toBeVisible(); + // Validate Organizations + await page.getByRole("menuitem", { name: "Organizations" }).click(); + await expect( + page.locator("#react-admin-title").getByText("Organizations"), + ).toBeVisible(); + // Validate VPCs + await page.getByRole("menuitem", { name: "VPCs" }).click(); + await expect(page.getByText("default vpc")).toBeVisible(); + await page.getByRole("cell", { name: "default vpc" }).click(); + await expect(page.getByText("100.64.0.0")).toBeVisible(); + // Validate Devices + await page.getByRole("menuitem", { name: "Devices" }).click(); + await expect( + page.locator("#react-admin-title").getByText("Devices"), + ).toBeVisible(); + // Validate Sites + await page.getByRole("menuitem", { name: "Sites" }).click(); + // Validate Invitations + await page.getByRole("menuitem", { name: "Invitations" }).click(); + await expect(page.getByLabel("Create")).toBeVisible(); + await page.getByLabel("Create").click(); + await expect(page.getByLabel("Email Address *")).toBeVisible(); + // Validate Security Groups + await page.getByRole("menuitem", { name: "Security Groups" }).click(); + await page.getByRole("cell", { name: "default vpc security group" }).click(); + await page.getByRole("button", { name: "Edit Rules" }).click(); + await page.getByRole("button", { name: "Add Rule" }).click(); + await page.getByRole("combobox").first().click(); + await page.getByRole("option", { name: "All ICMP" }).click(); + await page.getByRole("button", { name: "Save Rules" }).click(); + await page.getByRole("tab", { name: "Outbound Rules" }).click(); + await page.getByRole("button", { name: "Add Rule" }).click(); + await page.getByRole("combobox").first().click(); + await page.getByRole("option", { name: "All ICMP" }).click(); + await page.getByRole("button", { name: "Save Rules" }).click(); + await page.getByRole("tab", { name: "Inbound Rules" }).click(); + await expect(page.getByText("All ICMP")).toBeVisible(); + await page.getByRole("tab", { name: "Outbound Rules" }).click(); + await expect(page.getByText("All ICMP")).toBeVisible(); + await page.getByRole("button", { name: "Delete" }).click(); + await page.getByRole("tab", { name: "Inbound Rules" }).click(); + await page.getByRole("button", { name: "Delete" }).click(); + await page.getByRole("button", { name: "Save Rules" }).click(); + // Validate Registration Keys + await page.getByRole("menuitem", { name: "Registration Keys" }).click(); + await page.getByLabel("Create").click(); + await expect(page.getByLabel("Description")).toBeVisible(); + // Validate Logout + await page.getByLabel("Profile").click(); + await page.getByText("Logout").click(); + await expect(page.getByRole("button", { name: "Login" })).toBeVisible(); });