Upload Arduino IDE SC
This commit is contained in:
parent
734e5082d9
commit
fcef555386
|
@ -0,0 +1,393 @@
|
||||||
|
#include <Wire.h>
|
||||||
|
#include <LiquidCrystal_I2C.h>
|
||||||
|
#include <ESP8266WiFi.h>
|
||||||
|
#include <Firebase_ESP_Client.h>
|
||||||
|
#include <ArduinoJson.h>
|
||||||
|
|
||||||
|
// --- Konfigurasi WiFi ---
|
||||||
|
#define WIFI_SSID "LMAO"
|
||||||
|
#define WIFI_PASSWORD "awokawok"
|
||||||
|
|
||||||
|
// --- Konfigurasi Firebase ---
|
||||||
|
#define FIREBASE_HOST "https://meja-iotv0-default-rtdb.firebaseio.com/"
|
||||||
|
#define FIREBASE_AUTH "AIzaSyC9YQJWMSajJQTlh1tGLRcIeui4rqGvl8E"
|
||||||
|
|
||||||
|
FirebaseData firebaseData;
|
||||||
|
FirebaseConfig config;
|
||||||
|
FirebaseAuth auth;
|
||||||
|
|
||||||
|
LiquidCrystal_I2C lcd(0x27, 16, 2);
|
||||||
|
|
||||||
|
// --- Definisi Pin ---
|
||||||
|
#define TRIG_PIN D5
|
||||||
|
int echoPins[4] = {D6, D7, D8, D4};
|
||||||
|
#define BUZZER_PIN D0
|
||||||
|
#define FORCE_BUTTON_PIN D3
|
||||||
|
|
||||||
|
float distances[4];
|
||||||
|
float lastValidDistances[4];
|
||||||
|
float lastSensorDistances[4];
|
||||||
|
|
||||||
|
// --- Sistem Reservasi ---
|
||||||
|
bool tableActivationSensorActive = false;
|
||||||
|
bool tableReserved = false;
|
||||||
|
bool tableOccupied = false;
|
||||||
|
bool refreshTriggered = false;
|
||||||
|
|
||||||
|
unsigned long reservationSessionStartOrRefreshTime = 0;
|
||||||
|
unsigned long lastPresenceTime = 0;
|
||||||
|
|
||||||
|
String tempLine1 = "";
|
||||||
|
String tempLine2 = "";
|
||||||
|
unsigned long messageStartTime = 0;
|
||||||
|
unsigned long messageDuration = 2000;
|
||||||
|
bool showTempMessage = false;
|
||||||
|
String lastLine1 = "";
|
||||||
|
String lastLine2 = "";
|
||||||
|
unsigned long lastLCDUpdate = 0;
|
||||||
|
const unsigned long LCD_UPDATE_INTERVAL = 1000;
|
||||||
|
|
||||||
|
const unsigned long RESERVATION_ACTIVE_DURATION = 30 * 60 * 1000;
|
||||||
|
const unsigned long IDLE_OCCUPIED_TIMEOUT = 10 * 60 * 1000;
|
||||||
|
const unsigned long WARNING_TIME_10_MIN = 10 * 60 * 1000;
|
||||||
|
const unsigned long WARNING_TIME_5_MIN = 5 * 60 * 1000;
|
||||||
|
|
||||||
|
bool warning10MinTriggered = false;
|
||||||
|
bool warning5MinTriggered = false;
|
||||||
|
|
||||||
|
const float PRESENCE_MIN = 50.0;
|
||||||
|
const float PRESENCE_MAX = 80.0;
|
||||||
|
const float MOTION_THRESHOLD = 5.0;
|
||||||
|
|
||||||
|
bool presenceDetected = false;
|
||||||
|
|
||||||
|
String customerName = "N/A";
|
||||||
|
|
||||||
|
bool forceShutdownRequested = false;
|
||||||
|
unsigned long lastButtonPress = 0;
|
||||||
|
const unsigned long BUTTON_DEBOUNCE = 200;
|
||||||
|
unsigned long lastFirebaseUpdate = 0;
|
||||||
|
const unsigned long FIREBASE_UPDATE_INTERVAL = 5000;
|
||||||
|
|
||||||
|
#define TABLE_ID "meja_001"
|
||||||
|
String deviceID = TABLE_ID;
|
||||||
|
|
||||||
|
void showTemporaryMessage(String line1, String line2, int duration = 2000) {
|
||||||
|
tempLine1 = line1;
|
||||||
|
tempLine2 = line2;
|
||||||
|
messageDuration = duration;
|
||||||
|
showTempMessage = true;
|
||||||
|
messageStartTime = millis();
|
||||||
|
|
||||||
|
lcd.clear();
|
||||||
|
lcd.setCursor(0, 0); lcd.print(tempLine1);
|
||||||
|
lcd.setCursor(0, 1); lcd.print(tempLine2);
|
||||||
|
|
||||||
|
lastLCDUpdate = millis(); // supaya tidak update terlalu cepat
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==== SETUP ====
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(115200);
|
||||||
|
WiFi.mode(WIFI_STA);
|
||||||
|
lcd.init(); lcd.backlight();
|
||||||
|
pinMode(TRIG_PIN, OUTPUT);
|
||||||
|
pinMode(BUZZER_PIN, OUTPUT);
|
||||||
|
pinMode(FORCE_BUTTON_PIN, INPUT_PULLUP);
|
||||||
|
for (int i = 0; i < 4; i++) pinMode(echoPins[i], INPUT);
|
||||||
|
|
||||||
|
connectWiFi();
|
||||||
|
if (WiFi.status() == WL_CONNECTED) initFirebase();
|
||||||
|
showTemporaryMessage("Sistem Siap!", deviceID);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==== LOOP ====
|
||||||
|
void loop() {
|
||||||
|
if (WiFi.status() != WL_CONNECTED) connectWiFi();
|
||||||
|
if (Firebase.ready() && millis() - lastFirebaseUpdate > FIREBASE_UPDATE_INTERVAL) {
|
||||||
|
updateFromFirebase();
|
||||||
|
updateToFirebase();
|
||||||
|
lastFirebaseUpdate = millis();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (forceShutdownRequested) {
|
||||||
|
forceShutdown();
|
||||||
|
if (Firebase.ready()) {
|
||||||
|
Firebase.RTDB.setInt(&firebaseData, "/" + deviceID + "/force_shutdown", 0);
|
||||||
|
}
|
||||||
|
forceShutdownRequested = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (digitalRead(FORCE_BUTTON_PIN) == LOW && millis() - lastButtonPress > BUTTON_DEBOUNCE) {
|
||||||
|
forceShutdown(); lastButtonPress = millis();
|
||||||
|
}
|
||||||
|
|
||||||
|
readSensors();
|
||||||
|
analyzeActivity();
|
||||||
|
if (tableActivationSensorActive) {
|
||||||
|
if (!tableReserved) startReservation();
|
||||||
|
if (tableReserved) {
|
||||||
|
checkSessionTimeout();
|
||||||
|
checkIdle();
|
||||||
|
checkWarnings();
|
||||||
|
}
|
||||||
|
} else if (tableReserved) {
|
||||||
|
endReservation();
|
||||||
|
}
|
||||||
|
displayLCD();
|
||||||
|
printSerial();
|
||||||
|
delay(500);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==== SENSOR & AKTIVITAS ====
|
||||||
|
float readDistance(int pin) {
|
||||||
|
float sum = 0; int valid = 0;
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
digitalWrite(TRIG_PIN, LOW); delayMicroseconds(2);
|
||||||
|
digitalWrite(TRIG_PIN, HIGH); delayMicroseconds(10);
|
||||||
|
digitalWrite(TRIG_PIN, LOW);
|
||||||
|
long d = pulseIn(pin, HIGH, 30000);
|
||||||
|
float dist = d * 0.034 / 2;
|
||||||
|
if (dist > 2 && dist < 400) { sum += dist; valid++; }
|
||||||
|
delay(10);
|
||||||
|
}
|
||||||
|
return (valid == 0) ? 0 : sum / valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
void readSensors() {
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
float d = readDistance(echoPins[i]);
|
||||||
|
if (d > 0) lastValidDistances[i] = d;
|
||||||
|
distances[i] = lastValidDistances[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void analyzeActivity() {
|
||||||
|
unsigned long now = millis();
|
||||||
|
bool motion = false;
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
float delta = abs(distances[i] - lastSensorDistances[i]);
|
||||||
|
if (lastSensorDistances[i] > 0 && delta >= MOTION_THRESHOLD) motion = true;
|
||||||
|
lastSensorDistances[i] = distances[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (now - reservationSessionStartOrRefreshTime > 10000) refreshTriggered = false;
|
||||||
|
|
||||||
|
if (tableReserved && motion && !refreshTriggered) {
|
||||||
|
reservationSessionStartOrRefreshTime = now;
|
||||||
|
warning10MinTriggered = warning5MinTriggered = false;
|
||||||
|
refreshTriggered = true;
|
||||||
|
Serial.println("Sesi diperpanjang karena gerakan.");
|
||||||
|
}
|
||||||
|
|
||||||
|
presenceDetected = false;
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
if (distances[i] >= PRESENCE_MIN && distances[i] <= PRESENCE_MAX) {
|
||||||
|
presenceDetected = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (presenceDetected) {
|
||||||
|
lastPresenceTime = now;
|
||||||
|
tableOccupied = true;
|
||||||
|
} else if (now - lastPresenceTime > IDLE_OCCUPIED_TIMEOUT) {
|
||||||
|
tableOccupied = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tambahkan warning jika terlalu dekat
|
||||||
|
static unsigned long lastWarningTime = 0;
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
if (distances[i] > 0 && distances[i] < 10.0 && millis() - lastWarningTime > 5000) {
|
||||||
|
showTooCloseWarning();
|
||||||
|
lastWarningTime = millis();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void showTooCloseWarning() {
|
||||||
|
showTemporaryMessage("!! TERLALU DEKAT !!", "Jarak < 10 cm");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==== WIFI & FIREBASE ====
|
||||||
|
void connectWiFi() {
|
||||||
|
lcd.clear(); lcd.setCursor(0, 0); lcd.print("Koneksi WiFi...");
|
||||||
|
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
|
||||||
|
for (int i = 0; i < 30 && WiFi.status() != WL_CONNECTED; i++) {
|
||||||
|
delay(500); Serial.print(".");
|
||||||
|
}
|
||||||
|
lcd.clear();
|
||||||
|
if (WiFi.status() == WL_CONNECTED) {
|
||||||
|
showTemporaryMessage("WiFi Terhubung!", WiFi.localIP().toString());
|
||||||
|
Serial.println("WiFi IP: " + WiFi.localIP().toString());
|
||||||
|
} else {
|
||||||
|
showTemporaryMessage("Gagal WiFi!", "Coba lagi...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void initFirebase() {
|
||||||
|
config.host = FIREBASE_HOST;
|
||||||
|
config.signer.tokens.legacy_token = FIREBASE_AUTH;
|
||||||
|
Firebase.begin(&config, &auth);
|
||||||
|
Firebase.reconnectWiFi(true);
|
||||||
|
Serial.println(Firebase.ready() ? "Firebase Terhubung!" : "Gagal Firebase!");
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateFromFirebase() {
|
||||||
|
String base = "/" + deviceID;
|
||||||
|
if (Firebase.RTDB.getInt(&firebaseData, base + "/sensors/table_activation_sensor_active"))
|
||||||
|
tableActivationSensorActive = firebaseData.intData() == 1;
|
||||||
|
else
|
||||||
|
tableActivationSensorActive = false;
|
||||||
|
|
||||||
|
if (Firebase.RTDB.getString(&firebaseData, base + "/reserved_by")) {
|
||||||
|
customerName = firebaseData.stringData();
|
||||||
|
if (customerName.length() > 8) customerName = customerName.substring(0, 8);
|
||||||
|
} else customerName = "N/A";
|
||||||
|
|
||||||
|
if (Firebase.RTDB.getInt(&firebaseData, base + "/force_shutdown")) {
|
||||||
|
forceShutdownRequested = firebaseData.intData() == 1;
|
||||||
|
} else {
|
||||||
|
forceShutdownRequested = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateToFirebase() {
|
||||||
|
if (!Firebase.ready()) return;
|
||||||
|
String path = "/" + deviceID;
|
||||||
|
FirebaseJson json;
|
||||||
|
json.set("device_id", deviceID);
|
||||||
|
json.set("mac_address", WiFi.macAddress());
|
||||||
|
json.set("last_update", millis() / 1000);
|
||||||
|
json.set("table_occupied", presenceDetected ? 1 : 0);
|
||||||
|
json.set("time_remaining_minutes", (int)(getRemainingSessionTime() / 60000));
|
||||||
|
for (int i = 0; i < 4; i++) json.set("sensors/s" + String(i + 1), (int)distances[i]);
|
||||||
|
json.set("sensors/table_activation_sensor_active", tableActivationSensorActive ? 1 : 0);
|
||||||
|
Firebase.RTDB.updateNode(&firebaseData, path, &json);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==== RESERVASI ====
|
||||||
|
void resetFlags() {
|
||||||
|
for (int i = 0; i < 4; i++) lastSensorDistances[i] = 0;
|
||||||
|
warning10MinTriggered = warning5MinTriggered = refreshTriggered = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void startReservation() {
|
||||||
|
tableReserved = true;
|
||||||
|
reservationSessionStartOrRefreshTime = millis();
|
||||||
|
lastPresenceTime = millis();
|
||||||
|
resetFlags();
|
||||||
|
buzzerAlert(2, 500, 150);
|
||||||
|
Serial.println("Reservasi dimulai oleh " + customerName);
|
||||||
|
showTemporaryMessage("MEJA " + deviceID.substring(5), "RVSP: " + customerName);
|
||||||
|
}
|
||||||
|
|
||||||
|
void endReservation() {
|
||||||
|
tableReserved = false;
|
||||||
|
tableOccupied = false;
|
||||||
|
resetFlags();
|
||||||
|
showTemporaryMessage("RESERVASI", "BERAKHIR");
|
||||||
|
}
|
||||||
|
|
||||||
|
void forceShutdown() {
|
||||||
|
tableActivationSensorActive = false;
|
||||||
|
endReservation();
|
||||||
|
Firebase.RTDB.setInt(&firebaseData, "/" + deviceID + "/sensors/table_activation_sensor_active", 0);
|
||||||
|
Firebase.RTDB.setString(&firebaseData, "/" + deviceID + "/reserved_by", "N/A");
|
||||||
|
customerName = "N/A";
|
||||||
|
buzzerAlert(5, 100, 50);
|
||||||
|
lcd.clear(); lcd.setCursor(0, 0); lcd.print("TERIMA KASIH");
|
||||||
|
lcd.setCursor(0, 1); lcd.print("SUDAH DATANG!"); delay(3000);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long getRemainingSessionTime() {
|
||||||
|
if (!tableReserved) return 0;
|
||||||
|
unsigned long elapsed = millis() - reservationSessionStartOrRefreshTime;
|
||||||
|
return (elapsed >= RESERVATION_ACTIVE_DURATION) ? 0 : RESERVATION_ACTIVE_DURATION - elapsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void checkSessionTimeout() {
|
||||||
|
if (getRemainingSessionTime() == 0) {
|
||||||
|
tableActivationSensorActive = false;
|
||||||
|
endReservation();
|
||||||
|
Firebase.RTDB.setInt(&firebaseData, "/" + deviceID + "/sensors/table_activation_sensor_active", 0);
|
||||||
|
Firebase.RTDB.setString(&firebaseData, "/" + deviceID + "/reserved_by", "N/A");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void checkIdle() {
|
||||||
|
if (millis() - lastPresenceTime >= IDLE_OCCUPIED_TIMEOUT) tableOccupied = false;
|
||||||
|
else tableOccupied = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void checkWarnings() {
|
||||||
|
unsigned long remain = getRemainingSessionTime();
|
||||||
|
if (remain <= WARNING_TIME_5_MIN && !warning5MinTriggered) {
|
||||||
|
warning5MinTriggered = true;
|
||||||
|
buzzerAlert(5, 200, 80);
|
||||||
|
showWarningLCD("< 5 menit!");
|
||||||
|
} else if (remain <= WARNING_TIME_10_MIN && !warning10MinTriggered) {
|
||||||
|
warning10MinTriggered = true;
|
||||||
|
buzzerAlert(3, 300, 100);
|
||||||
|
showWarningLCD("< 10 menit!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void showWarningLCD(String text) {
|
||||||
|
showTemporaryMessage("** PERINGATAN **", text);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==== TAMPILAN ====
|
||||||
|
void displayLCD() {
|
||||||
|
if (millis() - lastLCDUpdate < LCD_UPDATE_INTERVAL) return;
|
||||||
|
|
||||||
|
if (showTempMessage) {
|
||||||
|
if (millis() - messageStartTime >= messageDuration) {
|
||||||
|
showTempMessage = false;
|
||||||
|
lastLine1 = ""; // paksa refresh ke tampilan normal
|
||||||
|
lastLine2 = "";
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String line1 = "MEJA " + deviceID.substring(5);
|
||||||
|
String line2;
|
||||||
|
|
||||||
|
if (WiFi.status() != WL_CONNECTED) line2 = "WiFi terputus";
|
||||||
|
else if (!tableReserved) line2 = "TERSEDIA";
|
||||||
|
else line2 = "RVSP: " + customerName;
|
||||||
|
|
||||||
|
if (line1 != lastLine1 || line2 != lastLine2) {
|
||||||
|
lcd.clear();
|
||||||
|
lcd.setCursor(0, 0); lcd.print(line1);
|
||||||
|
lcd.setCursor(0, 1); lcd.print(line2);
|
||||||
|
lastLine1 = line1;
|
||||||
|
lastLine2 = line2;
|
||||||
|
lastLCDUpdate = millis();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void printSerial() {
|
||||||
|
Serial.println("\n=== STATUS " + deviceID + " ===");
|
||||||
|
Serial.println("WiFi: " + String(WiFi.status() == WL_CONNECTED ? "YA" : "TIDAK"));
|
||||||
|
Serial.println("Firebase: " + String(Firebase.ready() ? "YA" : "TIDAK"));
|
||||||
|
Serial.print("Sensor: ");
|
||||||
|
for (int i = 0; i < 4; i++) Serial.print("S" + String(i+1) + ":" + String(distances[i]) + " ");
|
||||||
|
Serial.println();
|
||||||
|
Serial.println("Aktif: " + String(tableActivationSensorActive ? "YA" : "TIDAK"));
|
||||||
|
Serial.println("Reserved: " + String(tableReserved ? "YA" : "TIDAK"));
|
||||||
|
Serial.println("Occupied: " + String(tableOccupied ? "YA" : "TIDAK"));
|
||||||
|
Serial.println("Sisa Waktu: " + String(getRemainingSessionTime() / 60000) + " menit");
|
||||||
|
Serial.println("Pemesan: " + customerName);
|
||||||
|
Serial.println("Warning10: " + String(warning10MinTriggered));
|
||||||
|
Serial.println("Warning5: " + String(warning5MinTriggered));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==== BUZZER ====
|
||||||
|
void buzzerAlert(int count, int onTime, int offTime) {
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
digitalWrite(BUZZER_PIN, HIGH); delay(onTime);
|
||||||
|
digitalWrite(BUZZER_PIN, LOW); delay(offTime);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,49 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
use App\Livewire\Auth\Login;
|
|
||||||
use App\Models\User;
|
|
||||||
use Livewire\Livewire;
|
|
||||||
|
|
||||||
test('login screen can be rendered', function () {
|
|
||||||
$response = $this->get('/login');
|
|
||||||
|
|
||||||
$response->assertStatus(200);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('users can authenticate using the login screen', function () {
|
|
||||||
$user = User::factory()->create();
|
|
||||||
|
|
||||||
$response = Livewire::test(Login::class)
|
|
||||||
->set('email', $user->email)
|
|
||||||
->set('password', 'password')
|
|
||||||
->call('login');
|
|
||||||
|
|
||||||
$response
|
|
||||||
->assertHasNoErrors()
|
|
||||||
->assertRedirect(route('dashboard', absolute: false));
|
|
||||||
|
|
||||||
$this->assertAuthenticated();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('users can not authenticate with invalid password', function () {
|
|
||||||
$user = User::factory()->create();
|
|
||||||
|
|
||||||
$response = Livewire::test(Login::class)
|
|
||||||
->set('email', $user->email)
|
|
||||||
->set('password', 'wrong-password')
|
|
||||||
->call('login');
|
|
||||||
|
|
||||||
$response->assertHasErrors('email');
|
|
||||||
|
|
||||||
$this->assertGuest();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('users can logout', function () {
|
|
||||||
$user = User::factory()->create();
|
|
||||||
|
|
||||||
$response = $this->actingAs($user)->post('/logout');
|
|
||||||
|
|
||||||
$response->assertRedirect('/');
|
|
||||||
|
|
||||||
$this->assertGuest();
|
|
||||||
});
|
|
|
@ -1,47 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
use App\Models\User;
|
|
||||||
use Illuminate\Auth\Events\Verified;
|
|
||||||
use Illuminate\Support\Facades\Event;
|
|
||||||
use Illuminate\Support\Facades\URL;
|
|
||||||
|
|
||||||
test('email verification screen can be rendered', function () {
|
|
||||||
$user = User::factory()->unverified()->create();
|
|
||||||
|
|
||||||
$response = $this->actingAs($user)->get('/verify-email');
|
|
||||||
|
|
||||||
$response->assertStatus(200);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('email can be verified', function () {
|
|
||||||
$user = User::factory()->unverified()->create();
|
|
||||||
|
|
||||||
Event::fake();
|
|
||||||
|
|
||||||
$verificationUrl = URL::temporarySignedRoute(
|
|
||||||
'verification.verify',
|
|
||||||
now()->addMinutes(60),
|
|
||||||
['id' => $user->id, 'hash' => sha1($user->email)]
|
|
||||||
);
|
|
||||||
|
|
||||||
$response = $this->actingAs($user)->get($verificationUrl);
|
|
||||||
|
|
||||||
Event::assertDispatched(Verified::class);
|
|
||||||
|
|
||||||
expect($user->fresh()->hasVerifiedEmail())->toBeTrue();
|
|
||||||
$response->assertRedirect(route('dashboard', absolute: false).'?verified=1');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('email is not verified with invalid hash', function () {
|
|
||||||
$user = User::factory()->unverified()->create();
|
|
||||||
|
|
||||||
$verificationUrl = URL::temporarySignedRoute(
|
|
||||||
'verification.verify',
|
|
||||||
now()->addMinutes(60),
|
|
||||||
['id' => $user->id, 'hash' => sha1('wrong-email')]
|
|
||||||
);
|
|
||||||
|
|
||||||
$this->actingAs($user)->get($verificationUrl);
|
|
||||||
|
|
||||||
expect($user->fresh()->hasVerifiedEmail())->toBeFalse();
|
|
||||||
});
|
|
|
@ -1,39 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
use App\Livewire\Auth\ConfirmPassword;
|
|
||||||
use App\Models\User;
|
|
||||||
use Livewire\Livewire;
|
|
||||||
|
|
||||||
test('confirm password screen can be rendered', function () {
|
|
||||||
$user = User::factory()->create();
|
|
||||||
|
|
||||||
$response = $this->actingAs($user)->get('/confirm-password');
|
|
||||||
|
|
||||||
$response->assertStatus(200);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('password can be confirmed', function () {
|
|
||||||
$user = User::factory()->create();
|
|
||||||
|
|
||||||
$this->actingAs($user);
|
|
||||||
|
|
||||||
$response = Livewire::test(ConfirmPassword::class)
|
|
||||||
->set('password', 'password')
|
|
||||||
->call('confirmPassword');
|
|
||||||
|
|
||||||
$response
|
|
||||||
->assertHasNoErrors()
|
|
||||||
->assertRedirect(route('dashboard', absolute: false));
|
|
||||||
});
|
|
||||||
|
|
||||||
test('password is not confirmed with invalid password', function () {
|
|
||||||
$user = User::factory()->create();
|
|
||||||
|
|
||||||
$this->actingAs($user);
|
|
||||||
|
|
||||||
$response = Livewire::test(ConfirmPassword::class)
|
|
||||||
->set('password', 'wrong-password')
|
|
||||||
->call('confirmPassword');
|
|
||||||
|
|
||||||
$response->assertHasErrors(['password']);
|
|
||||||
});
|
|
|
@ -1,68 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
use App\Livewire\Auth\ForgotPassword;
|
|
||||||
use App\Livewire\Auth\ResetPassword;
|
|
||||||
use App\Models\User;
|
|
||||||
use Illuminate\Auth\Notifications\ResetPassword as ResetPasswordNotification;
|
|
||||||
use Illuminate\Support\Facades\Notification;
|
|
||||||
use Livewire\Livewire;
|
|
||||||
|
|
||||||
test('reset password link screen can be rendered', function () {
|
|
||||||
$response = $this->get('/forgot-password');
|
|
||||||
|
|
||||||
$response->assertStatus(200);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('reset password link can be requested', function () {
|
|
||||||
Notification::fake();
|
|
||||||
|
|
||||||
$user = User::factory()->create();
|
|
||||||
|
|
||||||
Livewire::test(ForgotPassword::class)
|
|
||||||
->set('email', $user->email)
|
|
||||||
->call('sendPasswordResetLink');
|
|
||||||
|
|
||||||
Notification::assertSentTo($user, ResetPasswordNotification::class);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('reset password screen can be rendered', function () {
|
|
||||||
Notification::fake();
|
|
||||||
|
|
||||||
$user = User::factory()->create();
|
|
||||||
|
|
||||||
Livewire::test(ForgotPassword::class)
|
|
||||||
->set('email', $user->email)
|
|
||||||
->call('sendPasswordResetLink');
|
|
||||||
|
|
||||||
Notification::assertSentTo($user, ResetPasswordNotification::class, function ($notification) {
|
|
||||||
$response = $this->get('/reset-password/'.$notification->token);
|
|
||||||
|
|
||||||
$response->assertStatus(200);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test('password can be reset with valid token', function () {
|
|
||||||
Notification::fake();
|
|
||||||
|
|
||||||
$user = User::factory()->create();
|
|
||||||
|
|
||||||
Livewire::test(ForgotPassword::class)
|
|
||||||
->set('email', $user->email)
|
|
||||||
->call('sendPasswordResetLink');
|
|
||||||
|
|
||||||
Notification::assertSentTo($user, ResetPasswordNotification::class, function ($notification) use ($user) {
|
|
||||||
$response = Livewire::test(ResetPassword::class, ['token' => $notification->token])
|
|
||||||
->set('email', $user->email)
|
|
||||||
->set('password', 'password')
|
|
||||||
->set('password_confirmation', 'password')
|
|
||||||
->call('resetPassword');
|
|
||||||
|
|
||||||
$response
|
|
||||||
->assertHasNoErrors()
|
|
||||||
->assertRedirect(route('login', absolute: false));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,25 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
use App\Livewire\Auth\Register;
|
|
||||||
use Livewire\Livewire;
|
|
||||||
|
|
||||||
test('registration screen can be rendered', function () {
|
|
||||||
$response = $this->get('/register');
|
|
||||||
|
|
||||||
$response->assertStatus(200);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('new users can register', function () {
|
|
||||||
$response = Livewire::test(Register::class)
|
|
||||||
->set('name', 'Test User')
|
|
||||||
->set('email', 'test@example.com')
|
|
||||||
->set('password', 'password')
|
|
||||||
->set('password_confirmation', 'password')
|
|
||||||
->call('register');
|
|
||||||
|
|
||||||
$response
|
|
||||||
->assertHasNoErrors()
|
|
||||||
->assertRedirect(route('dashboard', absolute: false));
|
|
||||||
|
|
||||||
$this->assertAuthenticated();
|
|
||||||
});
|
|
|
@ -1,13 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
use App\Models\User;
|
|
||||||
|
|
||||||
test('guests are redirected to the login page', function () {
|
|
||||||
$this->get('/dashboard')->assertRedirect('/login');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('authenticated users can visit the dashboard', function () {
|
|
||||||
$this->actingAs($user = User::factory()->create());
|
|
||||||
|
|
||||||
$this->get('/dashboard')->assertStatus(200);
|
|
||||||
});
|
|
|
@ -1,7 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
test('returns a successful response', function () {
|
|
||||||
$response = $this->get('/');
|
|
||||||
|
|
||||||
$response->assertStatus(200);
|
|
||||||
});
|
|
|
@ -1,40 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
use App\Livewire\Settings\Password;
|
|
||||||
use App\Models\User;
|
|
||||||
use Illuminate\Support\Facades\Hash;
|
|
||||||
use Livewire\Livewire;
|
|
||||||
|
|
||||||
test('password can be updated', function () {
|
|
||||||
$user = User::factory()->create([
|
|
||||||
'password' => Hash::make('password'),
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->actingAs($user);
|
|
||||||
|
|
||||||
$response = Livewire::test(Password::class)
|
|
||||||
->set('current_password', 'password')
|
|
||||||
->set('password', 'new-password')
|
|
||||||
->set('password_confirmation', 'new-password')
|
|
||||||
->call('updatePassword');
|
|
||||||
|
|
||||||
$response->assertHasNoErrors();
|
|
||||||
|
|
||||||
expect(Hash::check('new-password', $user->refresh()->password))->toBeTrue();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('correct password must be provided to update password', function () {
|
|
||||||
$user = User::factory()->create([
|
|
||||||
'password' => Hash::make('password'),
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->actingAs($user);
|
|
||||||
|
|
||||||
$response = Livewire::test(Password::class)
|
|
||||||
->set('current_password', 'wrong-password')
|
|
||||||
->set('password', 'new-password')
|
|
||||||
->set('password_confirmation', 'new-password')
|
|
||||||
->call('updatePassword');
|
|
||||||
|
|
||||||
$response->assertHasErrors(['current_password']);
|
|
||||||
});
|
|
|
@ -1,76 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
use App\Livewire\Settings\Profile;
|
|
||||||
use App\Models\User;
|
|
||||||
use Livewire\Livewire;
|
|
||||||
|
|
||||||
test('profile page is displayed', function () {
|
|
||||||
$this->actingAs($user = User::factory()->create());
|
|
||||||
|
|
||||||
$this->get('/settings/profile')->assertOk();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('profile information can be updated', function () {
|
|
||||||
$user = User::factory()->create();
|
|
||||||
|
|
||||||
$this->actingAs($user);
|
|
||||||
|
|
||||||
$response = Livewire::test(Profile::class)
|
|
||||||
->set('name', 'Test User')
|
|
||||||
->set('email', 'test@example.com')
|
|
||||||
->call('updateProfileInformation');
|
|
||||||
|
|
||||||
$response->assertHasNoErrors();
|
|
||||||
|
|
||||||
$user->refresh();
|
|
||||||
|
|
||||||
expect($user->name)->toEqual('Test User');
|
|
||||||
expect($user->email)->toEqual('test@example.com');
|
|
||||||
expect($user->email_verified_at)->toBeNull();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('email verification status is unchanged when email address is unchanged', function () {
|
|
||||||
$user = User::factory()->create();
|
|
||||||
|
|
||||||
$this->actingAs($user);
|
|
||||||
|
|
||||||
$response = Livewire::test(Profile::class)
|
|
||||||
->set('name', 'Test User')
|
|
||||||
->set('email', $user->email)
|
|
||||||
->call('updateProfileInformation');
|
|
||||||
|
|
||||||
$response->assertHasNoErrors();
|
|
||||||
|
|
||||||
expect($user->refresh()->email_verified_at)->not->toBeNull();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('user can delete their account', function () {
|
|
||||||
$user = User::factory()->create();
|
|
||||||
|
|
||||||
$this->actingAs($user);
|
|
||||||
|
|
||||||
$response = Livewire::test('settings.delete-user-form')
|
|
||||||
->set('password', 'password')
|
|
||||||
->call('deleteUser');
|
|
||||||
|
|
||||||
$response
|
|
||||||
->assertHasNoErrors()
|
|
||||||
->assertRedirect('/');
|
|
||||||
|
|
||||||
expect($user->fresh())->toBeNull();
|
|
||||||
expect(auth()->check())->toBeFalse();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('correct password must be provided to delete account', function () {
|
|
||||||
$user = User::factory()->create();
|
|
||||||
|
|
||||||
$this->actingAs($user);
|
|
||||||
|
|
||||||
$response = Livewire::test('settings.delete-user-form')
|
|
||||||
->set('password', 'wrong-password')
|
|
||||||
->call('deleteUser');
|
|
||||||
|
|
||||||
$response->assertHasErrors(['password']);
|
|
||||||
|
|
||||||
expect($user->fresh())->not->toBeNull();
|
|
||||||
});
|
|
|
@ -1,47 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
| Test Case
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| The closure you provide to your test functions is always bound to a specific PHPUnit test
|
|
||||||
| case class. By default, that class is "PHPUnit\Framework\TestCase". Of course, you may
|
|
||||||
| need to change it using the "pest()" function to bind a different classes or traits.
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
|
|
||||||
pest()->extend(Tests\TestCase::class)
|
|
||||||
->use(Illuminate\Foundation\Testing\RefreshDatabase::class)
|
|
||||||
->in('Feature');
|
|
||||||
|
|
||||||
/*
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
| Expectations
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| When you're writing tests, you often need to check that values meet certain conditions. The
|
|
||||||
| "expect()" function gives you access to a set of "expectations" methods that you can use
|
|
||||||
| to assert different things. Of course, you may extend the Expectation API at any time.
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
|
|
||||||
expect()->extend('toBeOne', function () {
|
|
||||||
return $this->toBe(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
/*
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
| Functions
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| While Pest is very powerful out-of-the-box, you may have some testing code specific to your
|
|
||||||
| project that you don't want to repeat in every file. Here you can also expose helpers as
|
|
||||||
| global functions to help you to reduce the number of lines of code in your test files.
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
|
|
||||||
function something()
|
|
||||||
{
|
|
||||||
// ..
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Tests;
|
|
||||||
|
|
||||||
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
|
|
||||||
|
|
||||||
abstract class TestCase extends BaseTestCase
|
|
||||||
{
|
|
||||||
//
|
|
||||||
}
|
|
|
@ -1,5 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
test('that true is true', function () {
|
|
||||||
expect(true)->toBeTrue();
|
|
||||||
});
|
|
Loading…
Reference in New Issue